/**
动态规划(Dynamic Programming)技术广泛应用于许多组合优化问题中
e.g.
1.Floyd
2.矩阵链的乘法
3.最大效益投资
4.背包问题
5.最长公共子序列问题
6.图像压缩
7.最大子段和
8.最优二分检索树
9.RNA的最有二级结构
关键词:
记忆花搜索
01背包问题
*/
///01背包问题 的 深搜穷竭 #include "cstdio" #include "algorithm" #define N 105 int n=4,weight=5; int w[N]={2,1,3,2},v[N]={3,2,4,2}; int rec(int i,int j) { int res; if(i==n) { res=0; } else if(j<w[i])///不能放 { res=rec(i+1,j); } else { res=std::max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);///放/不放 } return res; } int main() { printf("%d\n",rec(0,weight)); }
/**
搜索深度为n,每层搜索都需要2层分支,Perfect Tree结构,最坏需要O(2^n)
等比级数复杂度,n较大时,不可取
*/
用数组记忆下重复计算的部分,优化为O(nW)
#include "cstdio" #include "cstring" #include "algorithm" #define N 105 int n=4,weight=5; int w[N]={2,1,3,2},v[N]={3,2,4,2}; int dp[N+1][N+1]; int rec(int i,int j) { if(dp[i][j]>=0) { return dp[i][j]; } int res; if(i==n) { res=0; } else if(j<w[i]) { res=rec(i+1,j); } else { res=std::max(rec(i+1,j),rec(i+1,j-w[i])+v[i]); } return dp[i][j]=res; } int main() { memset(dp,-1,sizeof(dp)); printf("%d\n",rec(0,weight)); }
迭代式(递推式)
#include "cstdio" #include "cstring" #include "algorithm" #define N 105 int n=4,weight=5; int w[N]={2,1,3,2},v[N]={3,2,4,2}; int dp[N+1][N+1]; void solve() { for(int i=n-1;i>=0;i--) { for(int j=0;j<=weight;j++) { if(j<w[i]) { dp[i][j]=dp[i+1][j]; } else { dp[i][j]=std::max(dp[i+1][j],dp[i+1][j-w[i]]+v[i]); } } } } int main() { memset(dp,0,sizeof(dp)); solve(); printf("%d",dp[0][weight]); }
i的循环正序
for(int i=0;i<=n-1;i++) { for(int j=0;j<=weight;j++) { if(j<w[i]) { dp[i+1][j]=dp[i][j]; } else { dp[i+1][j]=std::max(dp[i][j],dp[i][j-w[i]]+v[i]);///你还剩多少重量 } } }
状态转移式 (前i个物品中选取总重不超过j)
for(int i=0;i<=n-1;i++) { for(int j=0;j<=weight;j++) { dp[i+1][j]=std::max(dp[i+1][j],dp[i][j]); if(j+w[i]<=weight) { dp[i+1][j+w[i]]=std::max(dp[i+1][j+w[i]],dp[i][j]+v[i]);///你已经放了多少重量 } } }
同一问题可能有各种各样的解决方法
对此题综上:
1.搜索的记忆化(计算过的就记住)
2.递推关系的DP(1.放不下 2.能放下(放与不放哪个合算?) 此状态来自上一行的哪一个容量 具体看书上的图)
3.从状态转移考虑的DP(此状态将转移到下一行的哪一个容量)
经典的LCS问题
#include "cstdio" #include "algorithm" #define N 1005 int n=4,m=4; char s[N]="abvd",t[N]="ddsd"; int dp[N+1][N+1]; void solve() { for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { if(s[i]==t[j]) { dp[i+1][j+1]=dp[i][j]+1;///找到一个相同的+1 } else { dp[i+1][j+1]=std::max(dp[i+1][j],dp[i][j+1]);///否则往前走 } } } printf("%d\n",dp[n][m]); } int main() { solve(); }