DP系列——递推/递归+记忆化搜索
http://120.78.128.11/Problem.jsp?pid=1852
一开始我想,从上往下的话,可以用递归,搜索(dfs)所有可能的路径,代码如下:
int dfs(int i,int j){ if(i==n)//递归到最后一层时返回 return a[i][j];//a[i][j]存图,dp[i][j]存数字总和 return dp[i][j]=max(dfs(i+1,j),dfs(i,j+1))+a[i][j];//取向左边和向右边走较大的一个数 }
从第一层开始递归到最后一层再从最后一层往回递归,,一共递归2的n次方,显然会T,那么要怎么优化一下呢?
我们看到,在题中第三排的1会被第二排的3和8递归两次,经历了一次重复计算,这时我们可以加入一个判断,使dp[][]初始化为-1,如果判断到dp[i][j]不等于-1,那么这个数就是计算过的,不用再次重复递归了,具体代码如下:
int dfs(int i,int j){ if(i==n)//递归到最后一层时返回 return a[i][j]; if(dp[i][j]>=0)//dp[][]初始化为-1,如果“有记忆”,就不再递归 return dp[i][j]; return dp[i][j]=max(dfs(i+1,j),dfs(i,j+1))+a[i][j]; }
这样就实现了“记忆化搜索”,每一个点只计算一次dp[i][j],那么最终的复杂度就是n的平方。
同时,也可以按照DP的思想从下往上计算,每次取一行中最大的数,从最后一层一直递推到第一层,因为下一步只能走左下或者右下,所以和递归一样取左右最大的一个数:
for(int j=1;j<=n;j++) dp[n][j]=a[n][j];//先计算最后一层 for(int i=n;i>=1;i--){//从倒数第二层一直到第一层 for(int j=1;j<=i;j++){ dp[i][j]=a[i][j]+max(dp[i+1][j],dp[i+1][j+1]); } }
AC代码:
#include<stdio.h> #include<algorithm> using namespace std; int dp[105][105]; int a[105][105]; int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++){ for(int j=1;j<=i;j++){ scanf("%d",&a[i][j]); } } for(int j=1;j<=n;j++) dp[n][j]=a[n][j]; for(int i=n;i>=1;i--){ for(int j=1;j<=i;j++){ dp[i][j]=a[i][j]+max(dp[i+1][j],dp[i+1][j+1]); } } printf("%d\n",dp[1][1]); return 0; }
EOF