动态规划——数字三角形(递归or递推or记忆化搜索)
动态规划的核心就是状态和状态转移方程。
对于该题,需要用抽象的方法思考,把当前的位置(i,j)看成一个状态,然后定义状态的指标函数d(i,j)为从格子出发时能得到的最大和(包括格子本身的值)。
在这个状态定义下,原问题的解就是d(i,j).
下面看一下不同状态之间如何转移。从格子(i,j)出发有两种策略。如果向左走,则到(i+1,j)后需要求“从(i+1,j)出发能得到的最大和”这一问题,即d(i+1,j)。
类似的,往右走之后需要求解d(i+1,j+1).由于可以在这两个决策中自由选择,所以应选择其中较大者。换句话说就是得到状态转移方程
d(i,j)=a(i,j)+Max(d(i+1,j),d(i+1,j+1))
这样就保证了子树的最优,这个性质称为最优子结构。
用直接递归的方法计算状态转移方程,效率往往十分低下。其原因是相同的子问题被重复计算了多次。
因此我们对其优化采用了递推算法或者采用记忆搜索的方式。
递推计算就是把每个问题直接罗列出来,因此不会重复。
记忆搜索的程序依然是递归的,但是把计算结果放在了数组d中。开始将所有状态置为-1,只要是计算过的状态就就变成非负,因此只要判断是否d[i][j]>=0,就知道它是否计算过。
#include<iostream>
#include<string>
using namespace std;
void input(int a[][10],int n)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<=i;j++)
{
cin>>a[i][j];
}
}
}
int max(int a,int b)
{
if(a>b)
return a;
else
return b;
}
/*******************i表示当前行数,j表示当前列数,n表示规格****************************/
/**********d(i,j)表示状态,d(i,j)=a(i,j)+Max(d(i+1,j),d(i+1,j+1))为状态转移方程*********/
int d(int a[][10],int i,int j,int n)
{
if(i>=n)
return 0;
else
{
return a[i][j]+max(d(a,i+1,j,n),d(a,i+1,j+1,n));
}
}
/**************采用递推计算,i表示当前行数,j表示当前列数,n表示规格**********************************/
int d1(int a[][10],int n)
{
int i,j;
int b[10][10];
for(i=0;i<=n;i++)
b[n][i]=0; //将b(n,0~n)状态全部赋值为0
for(i=n-1;i>=0;i--)
{
for(j=0;j<=i;j++)
{
b[i][j]=a[i][j]+max(b[i+1][j],b[i+1][j+1]);
}
}
return b[0][0];
}
/*************采用记忆化搜索*************************/
int d2(int a[][10],int b[][10],int i,int j,int n)
{
if(b[i][j]>=0)
return b[i][j];
else
{
if(i>=n)
return 0;
else
return b[i][j]=a[i][j]+max(d2(a,b,i+1,j,n),d2(a,b,i+1,j+1,n));
}
}
int main()
{
int a[10][10];
int b[10][10];
memset(b,-1,sizeof(b));
int n;
cout<<"请输入总共有多少行"<<endl;
cin>>n;
input(a,n);
cout<<d(a,0,0,n)<<endl;
cout<<d1(a,n)<<endl;
cout<<d2(a,b,0,0,n)<<endl;
return 0;
}