动态规划【SDUT】

定义:动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。
特征

  1. 最优子结构:当问题的最优解包含了其子问题的最优解时,称该问题具有最优子结构性质。
  2. 重叠子问题:在用递归算法自顶向下解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。动态规划算法正是利用了这种子问题的重叠性质,对每一个子问题只解一次,而后将其解保存在一个表格中,在以后尽可能多地利用这些子问题的解。

比如数字三角形这道题,是我们做dp的一道入门题。

#include <iostream>
#include <algorithm>using namespace std;const int N = 510, INF = 1e9;int n, a[N][N], f[N][N];int main()
{scanf("%d", &n);for (int i = 1; i <= n; i ++ )for (int j = 1; j <= i; j ++ )scanf("%d", &a[i][j]);for (int i = 0; i <= n; i ++ )for (int j = 0; j <= i + 1; j ++ )f[i][j] = -INF;//用无穷小覆盖掉数组外边的数据 f[1][1] = a[1][1];for (int i = 2; i <= n; i ++ )for (int j = 1; j <= i; j ++ )f[i][j] = max(f[i - 1][j - 1] + a[i][j], f[i - 1][j] + a[i][j]);int res = -INF;for (int i = 1; i <= n; i ++ ) res = max(res, f[n][i]);printf("%d\n", res);return 0;
}

小鑫去爬山这道题也是一样的,初识动态转移方程。

#include <iostream>
#include <algorithm>using namespace std;const int N = 510, INF = 1e9;int n, a[N][N], f[N][N];int main()
{scanf("%d", &n);for (int i = 1; i <= n; i ++ )for (int j = 1; j <= i; j ++ )scanf("%d", &a[i][j]);for (int i = 0; i <= n; i ++ )for (int j = 0; j <= i; j ++ )f[i][j] = INF;//用无穷大覆盖掉数组外边的数据 for(int i=1;i<=n;i++){f[n][i]=a[n][i];}        for (int i = n-1; i >= 1; i -- )for (int j = 1; j <= i; j ++ )f[i][j] = min(f[i + 1][j ] + a[i][j], f[i + 1][j+1] + a[i][j]);printf("%d\n",f[1][1]);return 0;
}

对于这种算法,我们认为当一个问题被拆解为n个子问题时,这些子问题并非相互独立的。这不同于分治算法。
分治法算的思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。

分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。
基于这一点可以参考两个题。
马兰过河卒
取数字问题
这是dp的两道例题。
马兰过河卒是一道经典的动态规划,思路也比较简单。我们认为每个点的可行路径是与前边点的可行路径有关联,且符合动态规划特征。可以写出动态转移方程,然后判断限制条件即可。

#include<iostream>
using namespace std;
long long int f[20][20];
int n,i,j,x,q,y,mx,my,m;
int Mx[9]={-2,-1,1,2,2,1,-1,-2,0};
int My[9]={-1,-2,-2,-1,1,2,2,1,0};
bool judge(int x,int y)
{for(q=0;q<=8;q++){if(x==Mx[q]+mx&&y==My[q]+my)return true;//确认被马拦下 }  return false;
}
int main()
{   scanf("%d %d %d %d",&n,&m,&mx,&my);f[0][0]=1;for(i=0;i<=n;i++){for(j=0;j<=m;j++){   if(i==0&j==0)continue;else if(judge(i,j))f[i][j]=0;//最先判断是否可以走!!! else if(i==0)f[i][j]=f[i][j-1];else if(j==0)f[i][j]=f[i-1][j];else f[i][j]=f[i-1][j]+f[i][j-1];//状态转移 }} printf("%lld\n",f[n][m]);
}

取数字问题

Description
给定M×N的矩阵,其中的每个元素都是-10到10之间的整数。你的任务是从左上角(1,1)走到右下角(M,N),每一步只能够向右或者向下,并且不能够走出矩阵的范围。你所经过的方格里面的数字都必须被选取,请找出一条最合适的道路,使得在路上被选取的数字之和是尽可能小的正整数。
Input
输入第1行是两个整数M和N,(2<=M<=10,2<=N<=10),分别表示矩阵的行和列的数目。接下来M行,每行包括N个整数,就是矩阵中的每一行的N个元素。
Output
输出只有一行,就是一个整数,表示所选道路上数字之和所能达到的最小正整数。如果不能达到任何正整数,输出-1。
Sample
Input

2 2
0 2
1 0

Output

1

题解:这道题我与其比较有缘。第一次见是在新生赛的时候,那时候我不知道贪心,不知道dp,更不知道dfs。
那个时候我坚信的是新生赛第一道题大概是那么一个签到题吧,所以我就埋头苦做了一番。怎么说,我一开始的做法是右边与下边取最小值,小的加到和里面。就是贪心法(那个时候还不知道hiahia),然后样例也过了,一直WA了。我怕题目会有苛判条件,我改了这个贪心算法一个小时。一个小时的时候我突然发现当前最优并不代表全局最优,我应该找一种算法可以去递归的取最小值。然后我就误打误撞的写了动态规划的转移方程出来,就是每一步的答案值f[i][j]=min(f[i-1][j],f[i][j-1])+a[i][j];然后题目还是WA,这个时候一个半小时过去了,我一个题都没A,这还是第一个题,心态直接崩了。我那时候才知道自己多么的菜。
这个题的标准解法是dfs,而不是动态规划,虽然它被放到了SDUT动态规划里面,我觉得应该就是让我们去注意这一点。
因为题目说的是“使得在路上被选取的数字之和是尽可能小的正整数。”如果我用动态规划,可以解得最小数,而不是最小正数。
那我们遇到负数就要抛弃吗?那肯定不能抛弃,因为后边遇到正的抵消之后可能它正好是那么一个最小正整数。

就像是人生最开始的那一个选择,它可能是目前最好的选择,但它不一定是人生最好的选择。如果不把人生的每一种可能性都走一遍,永远不能确定哪一条路才是最精彩的。
所以可能小白现在很菜,将来也会变得很强呢!
正是因为一开始说的动态规划的子问题有重叠性,你在取舍的过程中就注定了你的子问题并不是一个独立的问题,它总是与前一个点相互关联。如果不是一个独立的问题,我们就无法加上“只保存正数的和”这个限制条件,最后dp的结果我们就无法确定它的正负。
这个时候我们不得不放弃动态规划这种思想,用另一种思想去考虑这道题。
所以我们就用DFS去遍历所有的可能性。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int m[20][20];
int min = 0x3f3f3f3f;
int a, b;
void search(int i,int j,int sum)
{sum += m[i][j];//走到一个格子,则加上格子中的数字;if (i<a)//如果,还没到最下边,则向下走search(i+1,j,sum);if (j<b)//如果,还没到最右边,则向右走search(i,j+1,sum);if (i==a&&j==b&&sum<min&&sum>0)//如果到达(a, b)并且sum的值已经变为正整数,则更新;min=sum;
}
int main()
{int i, j;scanf("%d %d", &a, &b);for(i=1;i<=a;i++){for(j=1;j<=b;j++)scanf("%d", &m[i][j]);}search(1, 1, 0);//调用函数,从(1,1)开始if(min==0x3f3f3f3f)//如果max的值保持不变,即不能达到任何正整数min = -1;printf("%d\n", min);return 0;
}

然后是跟路径有关的题递归的函数,走迷宫。

递归的函数

Description
给定一个函数 f(a, b, c):
如果 a ≤ 0 或 b ≤ 0 或 c ≤ 0 返回值为 1;
如果 a > 20 或 b > 20 或 c > 20 返回值为 f(20, 20, 20);
如果 a < b 并且 b < c 返回 f(a, b, c−1) + f(a, b−1, c−1) − f(a, b−1, c);
其它情况返回 f(a−1, b, c) + f(a−1, b−1, c) + f(a−1, b, c−1) − f(a-1, b-1, c-1)。
看起来简单的一个函数?你能做对吗?
Input
输入包含多组测试数据,对于每组测试数据:
输入只有一行为 3 个整数a, b, c(a, b, c < 30)。
Output
对于每组测试数据,输出函数的计算结果。
Sample
Input

1 1 1
2 2 2

Output

2
4

题解:这道题的话考察记忆化搜索
记忆化搜索实际上是递归来实现的,但是递归的过程中有许多的结果是被反复计算的,这样会大大降低算法的执行效率。而记忆化搜索是在递归的过程中,将已经计算出来的结果保存起来,当之后的计算用到的时候直接取出结果,避免重复运算,因此极大的提高了算法的效率。
举个例子:这是斐波那契数列的递归代码,定义简单不多赘述。

 long long f(int n){if(n==1||n==2) return 1;return f(n-1)+f(n-2);}

枚举一些斐波那契数列的计算次数:

可以看出计算f[5]的时候计算了三次,计算f[6]还是要计算f[5]的那三次,总共计算了五次。如果不把f[5]记录下来,那这相当于白白浪费掉这些时间。
先来看递归的函数这道题的正确代码:(本文很多代码是sdut acm学长写的,要说起来就是上课过程中转载的hiahia,别怪我没声明

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>using namespace std;
const int N = 25, INF = 0x3f3f3f3f;
int dp[N][N][N];
int f(int a, int b, int c) {if (a <= 0 || b <= 0 || c <= 0) return 1;if (a > 20 || b > 20 || c > 20) return f(20, 20, 20);if (dp[a][b][c] != INF) return dp[a][b][c];int res;if (a < b && b < c)res = f(a, b, c - 1) + f(a, b - 1, c - 1) - f(a, b - 1, c);elseres = f(a - 1, b, c) + f(a - 1, b - 1, c) + f(a - 1, b, c - 1) -f(a - 1, b - 1, c - 1);return dp[a][b][c] = res;
}int main() {int a, b, c;memset(dp, 0x3f3f3f3f, sizeof dp);while (~scanf("%d %d %d", &a, &b, &c)) {printf("%d\n", f(a, b, c));}return 0;
}

如果我不用dp这个数组去保存数据:(错误示例)

int f(int a, int b, int c) {if (a <= 0 || b <= 0 || c <= 0) return 1;if (a > 20 || b > 20 || c > 20) return f(20, 20, 20);int res;if (a < b && b < c)res = f(a, b, c - 1) + f(a, b - 1, c - 1) - f(a, b - 1, c);elseres = f(a - 1, b, c) + f(a - 1, b - 1, c) + f(a - 1, b, c - 1) -f(a - 1, b - 1, c - 1);return res;
}

结果会如何?
如果试过的同学都知道是TLE(hiahia)。这就是记忆化搜索的优势了,节省运行时间。
关于记忆化搜索推荐看一下落谷的一道题----滑雪

走迷宫

Description
有一个m×n格的迷宫(表示有m行、n列),其中有可走的也有不可走的,如果用1表示可以走,0表示不可以走,输入这m*n个数据和起始点、结束点(起始点和结束点都是用两个数据来描述的,分别表示这个点的行号和列号)。现在要你编程找出所有可行的道路,要求所走的路中没有重复的点,走时只能是上下左右四个方向。如果一条路都不可行,则输出相应信息(用-1表示无路)。
Input
第一行是两个数m,n(1< m, n< 15),接下来是m行n列由1和0组成的数据,最后两行是起始点和结束点。
Output
所有可行的路径,输出时按照左上右下的顺序。描述一个点时用(x,y)的形式,除开始点外,其他的都要用“->”表示。如果没有一条可行的路则输出-1。
Sample
Input

5 4
1 1 0 0
1 1 1 1
0 1 1 0
1 1 0 1
1 1 1 1
1 1
5 4

Output

(1,1)->(1,2)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4)
(1,1)->(1,2)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4)
(1,1)->(1,2)->(2,2)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4)
(1,1)->(1,2)->(2,2)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4)
(1,1)->(2,1)->(2,2)->(3,2)->(4,2)->(4,1)->(5,1)->(5,2)->(5,3)->(5,4)
(1,1)->(2,1)->(2,2)->(3,2)->(4,2)->(5,2)->(5,3)->(5,4)

思路的话就是从起点(x,y)开始判断,如果是0就代表遇上墙了,返回。如果是1就保存这个点的坐标。因为题目说不可重复走一个点,所以保存完就赋值为0即可,往下遍历其上下左右的坐标即可,实现递归。

# include<stdio.h>
# include<string.h>
int a[50][50];  //存数据
int dir[4][2]={{0,-1},{-1,0},{0,1},{1,0}};  //4个方向
int b[101];     //用于线性储存路径,两个为一组
int c,d,flag;void find(int x,int y,int ii);//x y为起始坐标 ,ii=0线性记录路径
int main( )
{int n,m,x,y,i,j,ii;while(scanf("%d %d",&n,&m) == 2){ii = flag = 0;memset(b,0,sizeof(b));memset(a,0,sizeof(a));//初始化0 使得迷宫周围形成墙壁 遇到0 就return  for(i=1;i<=n;i++)for(j=1;j<=m;j++)scanf("%d",&a[i][j]);//读入数据 scanf("%d %d %d %d",&x,&y,&c,&d); //x,y起点。c,d终点 find(x,y,0);if(!flag) printf("-1\n");//可走到终点flag会被赋值 若未走到终点则输出-1; }return 0;
}void find(int x,int y,int ii)
{int i;if(x == c && y == d )    //来到终点则输出{flag = 1;//判断有没有路可以来到终点for(i=0;i<ii;i+=2){printf("(%d,%d)",b[i],b[i+1]);printf("->");}printf("(%d,%d)\n",c,d);return;}if(!a[x][y])return ;    //到达边界或已走或墙壁返回for(i=0;i<4;i++)    //依次走4个方向{a[x][y] = 0;     //如果该方向可走,则标记已走,为0b[ii] = x;         //记下横坐标b[ii+1] = y;       //记下纵坐标 find(x+dir[i][0],y+dir[i][1],ii+2);   //横坐标和纵坐标遍历上下左右; a[x][y] = 1;      //恢复原位置的可走标签}
}

还有一道变形式的走迷宫,过两天补上。
其他dfs思路的题比如导弹防御,n皇后等等。
然后再写最长公共子序列这道题。

最长公共子序列问题

Description
给定两个序列 X={x1,x2,…,xm} 和 Y={y1,y2,…,yn},找出X和Y的最长公共子序列。

Input
输入数据有多组,每组有两行 ,每行为一个长度不超过500的字符串(输入全是大写英文字母(A,Z)),表示序列X和Y。

Output
每组输出一行,表示所求得的最长公共子序列的长度,若不存在公共子序列,则输出0。

Sample
Input

ABCBDAB
BDCABA

Output

4

题解思路:先把坐标轴上的点都默认赋值为0,相当于数组边界;假如先把ABCD按顺序与A字符比较,依次向下。此时只有A字符与A字符相等,给【A】【A】这一点对应坐标加1,而其他字符仍然是0(字符“A”与串“ABCD”最长公共子序列为1)。那我们看对应的【B】【A】这一点的数值应该是几???

应该也是1。因为B不与A相等,所以它并不是直接加1的。而它赋值的方式是看列“ABCD”中前一个字符"A"的最长子序列1,B只需要"继承"这个数量即可。

而【B】【B】这个点因为字符“AB”中B与“ABCD”中的那个“B”相等,所以就在之前【A】【A】的数值上继承并加1。

而对于【B】【C】这一点我们都知道“AB”与“ABCD”的最长公共子序列是2,所以遍历到【B】【C】或者【B】【D】答案都应该是2。那么【B】【C】这一点是如何去“继承”的呢?
我们可以很轻松的总结出规律,如果是字符不相等的点,继承相邻的最大值字符相等的点,对应之前相等点的数值加1

这样就可以用Dp的思想去递推表达式。

#include<bits/stdc++.h>
using namespace std;
char x[505], y[505];
int q[505][505];
int main()
{int i, j, length_x, length_y;while(~scanf("%s %s", x, y)){length_x = strlen(x);length_y = strlen(y);for(i = 0; i <= length_x; i++){/// 初始化状态数组q的第一列q[i][0] = 0;}for(j = 0; j <= length_y; j++){/// 初始化状态数组q的第一行q[0][j] = 0;}for(i = 1; i <= length_x; i++){///此处 i 最后应该等于 length_x 此时为x[length_x - 1]对应于q[length_x]for(j = 1; j <= length_y; j++){///此处 j 最后应该等于 length_j 此时为x[length_j - 1]对应于q[length_j]if(x[i - 1] == y[j - 1])q[i][j] = q[i - 1][j - 1] + 1;else { q[i][j]=max(q[i - 1][j], q[i][j - 1]);} }///取上方和左方的最大值, max(q[i - 1][j], q[i][j - 1])}printf("%d\n", q[length_x][length_y]);///输出对应最后一个字符的长度}return 0;
}

我看网上有的这个题还有别的形式。比如:这个多个字符串求最长公共子序列的形式。

这个题放到以后的博客里写,可以参考这位博主。这个求最长公共子序列的个数确实不是难事,但如果让你求出所有的最长公共子序列呢?如果LCS不唯一呢?那就需要用到回溯算法,去求你的路径。这里把函数的那一部分简单的写一下。

void all(int i,int j,string str) //回溯之前的路线
{while(i>0&&j>0){if(X[i-1]==Y[j-1]){//用数组把这个符号记录下来 X[i-1] ,可以开辟动态空间}i--;j--; //走到前一个相等的字符面前else{if(a[i-1][j]>a[i][j-1])//从大的方向来,回大的方向去; i--;else if(a[i-1][j]<a[i][j-1])j--;else{all(i-1,j,str);all(i,j-1;str);return;   }    } }reverse()//最后把记录下来的数组翻转一下就可以输出了;
}

然后是最长上升子序列这道题。

最长上升子序列

Description
一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序列(ai1, ai2, …, aiK),这里1<= i1 < i2 < … < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8)。

你的任务,就是对于给定的序列,求出最长上升子序列的长度。
Input
输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。
Output
最长上升子序列的长度。
Sample
Input

7
1 7 3 5 9 4 8

Output

4

这道题在今天结训赛开始前准备写,然后结训赛直接就被出在题目列表里了,说来也巧。我直接当新题做了,晚上补一波题解。这道题感觉不难,思路挺简单的,就是建立一个dp模型。题解放在代码中了,代码如下:

#include<stdio.h>
#include<string.h>
int main()
{int m[1008];//上升子序列个数int a[1008];//具体数值int n;scanf("%d", &n);for (int i = 0; i < n; i++){scanf("%d", &a[i]);m[i] = 1;//初始化1,每个序列数从1开始递增 }for (int i = 1; i < n; i++){int max = 0;for (int c = 0; c < i; c++)//找f(n-1)的过程!!! {if (a[i]>a[c] && m[c]>max)//遍历前边的数,使得前一个的值较小 max = m[c];//而且前一个值的上升子序列个数是最多的 }m[i] = max + 1;//这个值的上升子序列数就是前边最多的上升子序列数加1; }int max = 0;for (int i = 0; i < n;i++)if (m[i]>max)max = m[i];//找到在这一串数字里最多的那个子序列数m[x] :printf("%d\n", max);
}

还有另一个变形叫上升子序列。它比较简单,跟刚刚那道题思路完全一样,就把记录最长子序列的长度改成记录递增子序列的最大和就行了。题目描述不写了,基本同上题一致。

#include <iostream>
int n,a[10000],sum[10000],max;//sum记录递增子序列的最大和。
int main()
{  while(~scanf("%d",&n))
{   for(int i=0;i<n;i++){scanf("%d",&a[i]);}sum[0]=a[0];for(int i=1;i<n;i++){  int max=0;for(int j=0;j<i;j++){if(a[i]>a[j]&&sum[j]>max){max=sum[j];      }sum[i]=max+a[i];}}max=0;for(int i=0;i<n;i++){ if(sum[i]>max)max=sum[i];}printf("%d\n",max);   }
}

最后写一个反向建立dp的题目,免费馅饼。

免费馅饼

Description
都说天上不会掉馅饼,但有一天gameboy正走在回家的小径上,忽然天上掉下大把大把的馅饼。说来gameboy的人品实在是太好了,这馅饼别处都不掉,就掉落在他身旁的10米范围内。馅饼如果掉在了地上当然就不能吃了,所以gameboy马上卸下身上的背包去接。但由于小径两侧都不能站人,所以他只能在小径上接。由于gameboy平时老呆在房间里玩游戏,虽然在游戏中是个身手敏捷的高手,但在现实中运动神经特别迟钝,每秒种只有在移动不超过一米的范围内接住坠落的馅饼。现在给这条小径如图标上坐标:

为了使问题简化,假设在接下来的一段时间里,馅饼都掉落在0-10这11个位置。开始时gameboy站在5这个位置,因此在第一秒,他只能接到4,5,6这三个位置中期中一个位置上的馅饼。问gameboy最多可能接到多少个馅饼?(假设他的背包可以容纳无穷多个馅饼)

Input
输入数据有多组。每组数据的第一行为以正整数n(0 < n < 100000),表示有n个馅饼掉在这条小径上。在结下来的n行中,每行有两个整数x,T(0 <= T < 100000),表示在第T秒有一个馅饼掉在x点上。同一秒钟在同一点上可能掉下多个馅饼。n=0时输入结束。

Output
每一组输入数据对应一行输出。输出一个整数m,表示gameboy最多可能接到m个馅饼。

提示:本题的输入数据量比较大,建议用scanf读入,用cin可能会超时。

Sample
Input

6
5 1
4 1
6 1
7 2
7 2
8 3
0

Output

4

题解:我们知道dp方程是一种递归思想,在赋值的过程要满足无后效性。所以我们推出的结果,只跟前一个结果有关,而从不关心它怎么来的。所以我们可以看这样一组dp方程:

 后  =  前  +   后         假设所有自身的数值为1,已知前max=1,
max    max     自身        则根据后自身=1,计算出后max的结果为2。
同理              前  =  后  +   前               的结果也是2。max    max     自身

所以逆向建立Dp和正向建立是一样的,只有最后归结的位置不同。
这个题首先我想到的就是建立一个dp方程,下一个时刻的饼最大和总是上一个时刻的最大和+1,然后加个相邻位置的判断条件。我求到最后,我只知道一定是时间越长能接的饼越多,所以我遍历最后一秒去求max,结果不对。是因为我最后求出的max不知道是从哪一个起点走哪一条路径过来的,而题目限定必须从5这个位置出发,基于这一点我们不得不逆向建立Dp。因为每个点都是求相邻最大值赋值过来的,所以最后输出5这个位置的数值就可以了。

#include<bits/stdc++.h>
using namespace std;
int dp[100005][12];
int main()
{   int n,x,t,maxt,i,k;while(scanf("%d",&n)&&n){maxt=0;memset(dp,0,sizeof(dp));while(n--){scanf("%d %d",&x,&t);dp[t][x]++;//t代表着时刻  x表示掉落饼的位置; if(maxt<t) maxt=t; //maxt为最后的一秒 }for(i=maxt-1;i>=0;i--){for(k=0;k<=10;k++)//边界是0,取max自动舍去 dp[i][k]+= max(max(dp[i+1][k-1],dp[i+1][k]),dp[i+1][k+1]);//前一秒的max总是后一秒max加前一秒本身}   //对下一秒的 自身 相邻位置 取max //逆向思维 因为只能确定d[0][5]是开始;cout << dp[0][5] << endl;//逆向回溯到【0】【5】这个点一定是最大收益点; }}

新手上道,很多见解可能有误,多多包涵!

发布于2.4日,小年当天,祝大家新年快乐,万事如意!!!

动态规划[SDUT]全题解超详细注释哦!相关推荐

  1. SENet代码复现+超详细注释(PyTorch)

    在卷积网络中通道注意力经常用到SENet模块,来增强网络模型在通道权重的选择能力,进而提点.关于SENet的原理和具体细节,我们在上一篇已经详细的介绍了:经典神经网络论文超详细解读(七)--SENet ...

  2. CNN经典网络模型(四):GoogLeNet简介及代码实现(PyTorch超详细注释版)

    目录 一.开发背景 二.网络结构 三.模型特点 四.代码实现 1. model.py 2. train.py 3. predict.py 4. spilit_data.py 五.参考内容 一.开发背景 ...

  3. CNN经典网络模型(二):AlexNet简介及代码实现(PyTorch超详细注释版)

    目录 一.开发背景 二.网络结构 三.模型特点 四.代码实现 1. model.py 2. train.py 3. predict.py 4. spilit_data.py 五.参考内容 一.开发背景 ...

  4. 史上最全的Linux常用——目录和文件管理命令——收藏这一篇就够了!(超全,超详细)

    史上最全的Linux常用--目录和文件管理命令--收藏这一篇就够了!(超全,超详细) Linux目录结构 命令 查看文件内容:-cat 查看文件内容:-more 查看文件内容:-less 查看文件内容 ...

  5. ResNeXt代码复现+超详细注释(PyTorch)

    ResNeXt就是一种典型的混合模型,由基础的Inception+ResNet组合而成,本质在gruops分组卷积,核心创新点就是用一种平行堆叠相同拓扑结构的blocks代替原来 ResNet 的三层 ...

  6. 数据结构--链栈的c语言实现(超详细注释/实验报告)

    数据结构–链栈的c语言实现(超详细注释/实验报告) 知识小回顾 栈(Stack)作为一种限定性线性表,是将线性表的插入和删除操作限制为仅在表的一端进行,通常将表中允许进行插入.删除操作的一端成为栈顶( ...

  7. 冒泡排序Matlab程序超详细注释

    冒泡排序Matlab程序超详细注释 bubble_sort.m function y=bubble_sort(x) % %冒泡算法: x_len=length(x);%度量数量长度,为排序做准备 fo ...

  8. codevs 2924 数独挑战 x(三种做法+超详细注释~)

    2924 数独挑战  时间限制: 1 s  空间限制: 1000 KB  题目等级 : 钻石 Diamond 题目描述 Description "芬兰数学家因卡拉,花费3个月时间设计出了世界 ...

  9. Win10基于python,spleeter 人声提取工具安装和使用(全网最全,超详细)

    Win10基于python,spleeter 音频分轨工具安装和使用(全网最全,超详细) 小声BB(不想看可直接跳到正片) 碎碎念(写给小白) 正片开始 说明 总体的框架 详细步骤 1.安装pytho ...

  10. 高德地图刷新当前位置_再也不怕找错!高德地图更新1号线地铁出入口信息啦!超全!超详细!...

    原标题:再也不怕找错!高德地图更新1号线地铁出入口信息啦!超全!超详细! 为了方便市民乘坐地铁 济南轨道交通集团联合高德地图 将1号线全线车站位置和出入口编号 落在了地图上 是不是很贴心? 以后再也不 ...

最新文章

  1. (6)javascript的程序控制结构及语句-----(1)条件判断
  2. __cpuidex读取CPU序列号
  3. 引入mysql+命名空间_C#连接MySQL操作详细教程
  4. pythonpip安装与使用_Python pip 安装使用与问题
  5. 在sqlplus中操作blob和clob
  6. AOP—JVM SandBox—快速上手
  7. Message popover
  8. cve20190708补丁的kb名称_微软KB4495667补丁(CVE 2019-0708补丁)V1.1 官方版
  9. 除了ssh外也可以开启telnet服务连接服务器
  10. C语言,利用函数调用统计输出素数并统计素数和
  11. string 类的常用方法 substring indexof length
  12. php更换wordpress用户头像,WordPress主题设置在前台页面添加用户头像教程
  13. 如何解决“请考虑使用 app.config 将程序集“XXXXXXXX”从版本XXXX重新映射到版本XXXX”的问题
  14. 区块链NFT项目策划书
  15. 【non-coding RNA】 非编码RNA有哪些?
  16. 什么是肠漏综合征,它如何影响健康?
  17. 发布工程到私有仓库maven
  18. 数据库迁移工具Kettle连接Mysql数据库报错:Driver class ‘org.gjt.mm.mysql.Driver‘ could not be found, make sure the解决
  19. android8.0内置壁纸,一加手机8pro内置壁纸分享
  20. win11 右键菜单恢复成win10样式(亲测有效)

热门文章

  1. anaconda下载速度太慢_Anaconda更新下载速度慢的解决方法:更换源
  2. 扩展名是.ps的PostScript文件详解
  3. python 重定向 网址错_Python URL重定向问题
  4. Detail-revealing Deep Video Super-resolution 论文笔记
  5. Pyecharts库及其与Django的结合使用
  6. 计算机专业毕业设计工作日志,计算机科学技术系毕业设计工作日志.doc
  7. 手机话费充值和手机流量充值 API
  8. 一款开源免费的网站监控系统
  9. mac 桌面显示服务器,隐藏 Mac 桌面内容的三种方法 | 一日一技 · Mac
  10. 64位Win10 2004正式版_MSDN我告诉你win10 2004镜像下载