题意大致:
解题思路:这道题目可以用递归的方法解决。基本思路是:D(r,j)表示第 r 行第 j 个数字(r,j都从 1 开始算),以 MaxSum(r,j)代表从第 r 行的第 j 个数字到底边的最佳路径的数字之和,则本题目要求MaxSum(1,1)。
从某个D(i,j)出发,显然下一步只能走D(r+1,j)或 D(r+1,j+1)。如果走D(r+1,j),那么得到的MaxSum(r,j)就是MaxSum(r+1,j+1)+D(i,j);如果走D(r+1,j+1),那么得到的MaxSum(r,j)就是MaxSum(r+1,j+1)+D(r,j)。所以选择往哪里走就需要比较MaxSum(r+1,j)和MaxSum(r+1,j+1)哪个值更大。
首先我们第一反应有可能是用递归实现上面的思路,但是当你递归程序写出来的时候,你会发现时间复杂度太高了
/*
递归代码:
*/
#include<iostream>
#define MAX 101
using namespace std;
int D[MAX][MAX];
int n;
int MaxSum(int i,int j){
if(i == n){
cout<<"D[i][j] = "<<D[i][j]<<endl;
return D[i][j];
}
int x = MaxSum(i+1,j);
int y = MaxSum(i+1,j+1);
return max(x,y)+D[i][j];
}
int main(){
int i,j;
cin>>n;
for(i=1;i<=n;i++){
for(j=1;j<=i;j++){
cin>>D[i][j];
}
}
cout<<MaxSum(1,1);
return 0;
}
我们可以研究一下程序为什么时间复杂度高,我们仔细研究一下程序你会发现其实,这个递归程序里存在着很多重复计算,为了避免重复计算那么我们肯定首先会想到用记忆数组来存储它,每次计算的值,当值被计算过就直接返回值,不再进行更深一层的递归。
记忆数组代码:
#include<iostream>
#define MAX 101
using namespace std;
int D[MAX][MAX];
int n;
int maxSum[MAX][MAX];
int MaxSum(int i,int j){
if(maxSum[i][j] != -1)
return maxSum[i][j];
if(i == n){
// cout<<"D[i][j] = "<<D[i][j]<<endl;
maxSum[i][j] = D[i][j];
return D[i][j];
}
int x = MaxSum(i+1,j);
int y = MaxSum(i+1,j+1);
maxSum[i][j] = max(x,y)+D[i][j];
return maxSum[i][j];
}
int main(){
int i,j;
cin>>n;
for(i=1;i<=n;i++){
for(j=1;j<=i;j++){
cin>>D[i][j];
maxSum[i][j] = -1;
}
}
cout<<MaxSum(1,1);
return 0;
}
好,下面我们进入我们解这题的正题,其实我前面讲这么多都是为下面我们要讲的动态规划做铺垫
将一个问题分解为子问题递归求解,并且将中间结果保存以避免重复计算的办法,可以称为“动态规划”。动态规划通常用来求最优解。能用动态规划解决的求最优解的问题,必须满足最优解的每个局部解也是最优的。依次以为例,最佳路径中每个数字到底部的那一段路径都是从该数字出发到底部的最佳路径。
下面时把递归转化成递推的的过程
从MaxSum[N-1]行这一行元素开始向上逐行递推,就能求得最终maxSum[1][1]的值
/*
动态规划代码
*/
#include<iostream>
using namespace std;
int dp[100][100];
int n;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
cin>>dp[i][j];
}
}
for(int i=n;i>=2;i--){
for(int j=1;j<=i;j++){
dp[i-1][j] = max(dp[i-1][j]+dp[i][j],dp[i-1][j]+dp[i][j+1]);
}
}
cout<<dp[1][1]<<endl;
return 0;
}
实际上还可以再进行空间优化….
有什么不懂可以在评论区问我,我会及时回答的,感谢阅读,希望能帮到您!