DP-以数塔问题为例分析DP的一些基础知识
最近看了《算法笔记》,感觉里面的动态规划写的不错,综合自己的感想,写一写。DP用来解决最优化问题,而DC是解决问题的。动态规划将原始的问题分为若干个子问题,通过综合子问题的最优解,来得到原始问题的最优解。动态规划会将每个求解过的子问题的解记录下来,下次遇到同样的问题,可以直接使用。一般使用递归和递推的写法来写动态规划。
1.递推
5
8 3
12 7 16
4 10 11 6
从第一层走到第n层,路径上所有数字相加的和最大是多少。
令dp[i][j](我们称它为状态)表示从第i行第j列到最底层的路径上数字相加得到的最大和。想求出dp[i][j],需求两个子问题,即从(i+1,j)到达最底层的最大和dp[i+1][j]和从(i+1,j+1)到达最底层的最大和dp[i+1][j+1]。所以dp[i][j]=max(dp[i+1][j+1],dp[i+1][j])+f[i][j](这个方程称之为状态转移方程)。数塔最后一层的dp值,等于元素本身,dp[n][j]=f[n][j],把这种可以直接确定结果的部分称之为边界。动态规划的递推写法就是从边界出发,通过状态转移方程扩散到整个dp数组。
#include<iostream> #include<algorithm> using namespace std; const int maxn=1000; int f[maxn][maxn],dp[maxn][maxn]; int main(){ int n; cin>>n; for(int i=1;i<=n;i++){ for(int j=1;j<=i;j++){ cin>>f[i][j]; } } for(int j=1;j<=n;j++){ dp[n][j]=f[n][j]; } for(int i=n-1;i>=1;i--){ for(int j=1;j<=i;j++){ dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+f[i][j]; } } cout<<"最大值:"<<dp[1][1]<<endl; cout<<"最大路径:"<<f[1][1]; int j=1; for(int i=2;i<=n;i++){ int value=dp[i-1][j]-f[i-1][j]; if(value==dp[i][j]) cout<<"->"<<f[i][j]; else cout<<"->"<<f[i][j+1]; j++; } return 0; }
2递归
使用备忘录的方式对斐波那契数列进行求解。
#include<iostream> #include<vector> using namespace std; vector<int>mem; int F(int n){ if(n==1||n==2) return 1; if(mem[n]!=-1) return mem[n]; else{ mem[n]=F(n-1)+F(n-2); return mem[n]; } } int main(){ int n; cin>>n; mem=vector<int>(n+1,-1); n=F(n); cout<<n; return 0; }