其实对动态规划并不是特别了解,在网上看到说动态规划是递归时去除重复的运算,提高效率。
今天结合“数字三角形”这道编程题和书本内容进行学习,题目可以自行百度。
数字三角形这道题非常经典,通过三个方法来探讨这道题的解题思路~_~
首先申请两个常数组,a[i][j]存储输入数字三角形的数字,d[i][j]为二次计算存储的值。
其中状态转移方程是:d[i][j]=a[i][j]+max(d[i+1][j],d[i+1][j+1])。
方程及各方法的理解方法是直接代入数值跟着走一遍。
方法一:递归计算
int solve(int i,int j){
return a[i][j]+(i==k?0:max(solve(i+1,j),solve(i+1,j+1))); //i==k?此处为边界判断
}
注:递归求解的过程有很多重复计算,效率低。得出的解存于a[1][1]中。
方法二:递推计算
int i,j;
for(j=1;j<=n;j++)d[n][j]=a[n][j];
for(i=n-1;i>=1;--i)
for(j=1;j<=i;++j)
d[i][j]=a[i][j]+max(d[i+1][j],d[i+1][j+1]);
注:递推求解的思路清晰,从最下层一直往上推算,得出最优解,储存于d[1][1]中。时间复杂度是O(n^2)。
方法三:动态规划(记忆化搜索)
memset(d,-1,sizeof(d));
int solve(int i,int j){
if(d[i][j]>=0)return d[i][j];
return d[i][j]=a[i][j]+(i==k?0:max(solve(i+1,j),solve(i+1,j+1))); //i==k?此处为边界判断
}
注:对比方法一,不难发现d[i][j]的作用是标记重复计算,大大地优化了程序的时间开销。得出的最优解,储存于d[1][1]中。
送上方法三的解题源码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int k;
int d[101][101],a[101][101];
int solve(int i,int j){
if(d[i][j]>=0)return d[i][j];
return d[i][j]=a[i][j]+(i==k?0:max(solve(i+1,j),solve(i+1,j+1)));
}
int main()
{
memset(d,-1,sizeof(d));
int i,j;
cin>>k;
for(i=1;i<=k;++i)
for(j=1;j<=i;++j)
cin>>a[i][j];
solve(1,1);
cout<<d[1][1]<<endl;
return 0;
}
此时发现空间开销一半都是浪费的,可以动态申请二维数组,但相对麻烦。
动态申请二维数组的方法:
cin>>row>>col;
int **p=new int*[row];
for(int i=0;i<row;++i)
p[i]=new int[col];
加油!!!
部分内容参考刘汝佳老师的《算法竞赛入门经典》