连接ios
一句话题目:给出一个n层的三角形,每一个位置有一个数字,到达后可得到,求到达最低层能达到的最大数字和。数组
题目分析:优化
首先咱们考虑能不能用搜索作,由于对于一个坐标,咱们只有向下的左边或者右边。对于一个三角形咱们进行特殊的处理,好比下面的三角形咱们能够处理成spa
13code
11 8blog
12 7 26递归
6 14 15 8get
12 7 13 24 11string
若是咱们到达了一个位置(i,j)咱们继续向下能够到达(i+1,j+1)和(i+1,j);io
那么搜索的代码:
#include <cstdio> const int maxn = 1010; int n, ans, cnt; int val[maxn][maxn]; void dfs(int i,int j){ if (i == n+1){ if (cnt > ans) ans = cnt; return ; } cnt += val[i][j]; dfs(i+1, j+1);dfs(i+1, j); cnt -= val[i][j]; } int main(){ scanf("%d", &n); for (int i = 1;i <= n;i ++) for (int j = 1;j <= i;j ++) scanf("%d", &val[i][j]); dfs(1, 1); printf("%d", ans); }
复杂度是2^n很明显超时(题目中n为1000);
咱们看一个简单的状况好比咱们在(i,j)这个位置,这个位置是由(i-1,j)转移过来,咱们已经求出了全部的(i,j)如下的点咱们向上返回到(i-1,j)这个位置,咱们就向下达到(i,j+1)咱们发现(i,j)能够到达(i+1,j+1)而且(i,j+1)也能够到达(i+1,j+1);那么这个点咱们就计算了两次,致使效率低下,若是咱们用一个数组表示到达(i,j)能获得的最大数值,就能够不用重复计算,这样就至关于把每一个点都遍历一遍,复杂度就是((1+n)*n/2) 就不会超时了,咱们把这个方法,叫作记忆化,而整个方法叫作记忆化搜索。
下面看代码:
#include <cstdio> #include <cstring> #include <iostream> const int maxn = 1010; using namespace std; int n; int val[maxn][maxn], f[maxn][maxn]; int dfs(int i,int j){ if (f[i][j] != -1) return f[i][j]; if (i == n+1) return f[i][j] = val[i][j]; return f[i][j] = max(dfs(i+1, j+1), dfs(i+1, j)) + val[i][j]; } int main(){ memset(f, -1, sizeof(f)); scanf("%d", &n); for(int i = 1;i <= n;i ++) for (int j = 1;j <= i;j ++) scanf("%d", &val[i][j]); printf("%d", dfs(1,1)); }
可是由于递归比递推自己慢因此咱们平时都不写递归,能够写成递推。咱们前面讲到的是f[i][j]表示从(i,j)出发能达到的最大值,如今咱们换种方式,用f[i][j]表示从(1,1)出发到达(i,j)的最大值,由于咱们发现(i,j)能够从(i-1,j)和(i-1,j-1)转移过来因此咱们按照f[i][j] = max(f[i-1][j-1], f[i-1][j]) + val[i][j];能够获得当前点的最大值,那么由于咱们在计算f[i][j]的时候必定要保证f[i-1][j-1]和f[i-1][j]已经计算过。因此咱们能够采用顺推的方法就是从上向下推。下面看代码:
#include <cstdio> #include <cstring> #include <iostream> const int maxn = 1010; using namespace std; int n, ans; int val[maxn][maxn], f[maxn][maxn]; int main(){ scanf("%d", &n); for (int i = 1;i <= n;i ++) for (int j = 1;j <= i;j ++) scanf("%d", &val[i][j]); for (int i = 1;i <= n;i ++) for (int j = 1;j <= i;j ++){ if (j == 1) f[i][j] = val[i][j] + f[i-1][j]; else if (j == n) f[i][j] = val[i][j] + f[i-1][j-1]; else f[i][j] = max(f[i-1][j], f[i-1][j-1]) + val[i][j]; } for (int i = 1;i <= n;i ++) ans = max(ans, f[n][i]); printf("%d", ans); }
考虑一个新的思想,从上到下求最大值,能够转换为从最后一排出发到达第一层的最大值,那么对于(i,j)能够从(i+1, j+1)和(i+1, j)过来,那么能够将整个递推倒过来写,就能够 不用最后一个for循环了。
代码:
#include <cstdio> #include <iostream> const int maxn = 1010; using namespace std; int n; int f[maxn][maxn], val[maxn][maxn]; int main(){ scanf("%d", &n); for (int i = 1;i <= n;i ++) for (int j = 1;j <= i;j ++) scanf("%d", &val[i][j]); for (int i = n;i >= 1;i --) for (int j = 1;j <= i;j ++) f[i][j] = max(f[i+1][j], f[i+1][j+1]) + val[i][j]; printf("%d", f[1][1]); return 0; }
下面对于动态规划进行总结:
1.首先定义状态,考虑有关的变量,而后进行定义
2.找出转移方程
3.进行优化,通常就是状态优化和转移优化。