一、题目描述
二、思路讲解
这是一道经典的动态规划入门题目,当然由于规模小,也可以用暴力解决,但我想通过一道简单的题目去了解动态规划的思想不失为一种较好的入门方式。
动态规划思想:
1、将问题分解为若干个子问题(这与分治思想大致相同)。
2、根据你划分子问题的方式,找出状态转移方程。
3、根据状态转移方程,选择解决方式。
那么,归结到底,我们是来解决问题的,但中国有句古话,叫做“万事开头难”,解决问题的第一步往往是思路上面的,没有思路,勿谈解题,那么从动态规划上我们通常有这么两种思路:
①带备忘的自顶向下法
②自底向上法
顾名思义,思路①通过记录算好的路径数据(如开辟数组),来向下继续求解路径数据,思路②则是需要你恰当的定义子问题的概念,使得任意子问题都依赖于更小的子问题,也就是当我们要求解的时候最小子问题已经解决(已知)。
思路已经明确,又到了求解状态转移方程的时候:
思路①:设数据存入数组data[][],路径状态记录数组record[][]
这个就很好理解,我们把需要知道的东西存放在数组中,有需要就拿出来用。
思路②:定义递归函数FindMaxPath(int i,int num),下面简称FMP,当然也可以不用
举个例子具体描述一下: 相比于带备忘的自顶向下法,自底向上法会有些难理解,比如我在学的时候就疑惑为啥就能知道最优解的底是10,而不是19,其实就是动态规划思想:局部最优并非全局最优,但全局最优时,局部一定最优,示例也可看出,相比于10+18为底的最优子路径,其余子路径都较差。
三、代码示例(非完全符合)
①带备忘的自顶向下法
#include<bits/stdc++.h>
using namespace std;
#define N 10
int a[N][N];
int Record[N][N];
int main(){
int i,j,n;
n=5;
for(i=0;i<n;i++)
for(j=0;j<i+1;j++){
scanf("%d",&a[i][j]);
}
for(i=0;i<n;i++){
for(j=0;j<i+1;j++){
printf("%d ",a[i][j]);
}
printf("\n");
}
Record[0][0]=a[0][0];
for(i=1;i<n;i++){
for(j=0;j<i+1;j++){
Record[i][j]=a[i][j]+max(Record[i-1][j],Record[i-1][j-1]);
}
}
for(i=0;i<n;i++){
for(j=0;j<i+1;j++){
printf("%d ",Record[i][j]);
}
printf("\n");
}
int MaxNum=Record[0][0];
for(i=0;i<n;i++)
for(j=0;j<i+1;j++){
if(MaxNum<Record[i][j])
MaxNum=Record[i][j];
}
cout<<MaxNum<<endl;
return 0;
}
②自底向上法
#include<bits/stdc++.h>
using namespace std;
#define N 10
int a[N][N];
int dp[N][N];
int main(){
int n=5;
int i,j;
for(i=0;i<n;i++)
for(j=0;j<i+1;j++)
scanf("%d",&a[i][j]);
for(i=0;i<n;i++)
dp[n-1][i]=a[n-1][i];
for(i=n-2;i>=0;i--)
for(j=0;j<=i;j++)
dp[i][j]=a[i][j]+max(dp[i+1][j],dp[i+1][j+1]);
int ans=dp[0][0];
cout<<ans<<endl;
return 0;
}