当前位置:   article > 正文

动态规划dp_dp[i][j]=dp[i-1][j] || dp[i-1][abs(j-w[i])] ||dp[i

dp[i][j]=dp[i-1][j] || dp[i-1][abs(j-w[i])] ||dp[i-1][j+w[i]];是什么意思

原文链接:https://woozie.blog/index.php/2022/10/09/26/

目录

背包

01背包(每个物品只有一个)

朴素01背包

01背包一维数组优化

完全背包(物品无限)

朴素完全背包

完全背包优化

完全背包一维优化

多重背包(物品有限)

区间dp

线性

环状

计数dp

线性dp

 


 

背包

 

01背包(每个物品只有一个)

朴素01背包

 

dp[i][j]的含义:前i个物品,容量为j的情况下能装物品的价值最大值

 

第一层循环——从1到n个物品

第二层循环——从容量0到m

对于第i个物品,当容量为j时

        1. v[i]>j  即物品大于容量,不能放,则继承  dp[i][j]=dp[i-1][j];

        2. v[i]<=j 选择最大价值  max(dp[i-1][j],dp[i-1][j-v[i]]+w[i])  //dp[i-1][j]为不放直接继承,dp[i-1][j-v[i]]+w[i]为放第i件物品

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1010;
  4. int n,m; //n物品个数,m背包容量
  5. int v[N],w[N]; //v物品所占空间,w物品价值
  6. int dp[N][N];
  7. int main()
  8. {
  9. scanf("%d%d",&n,&m);
  10. for(int i=1;i<=n;i++)
  11. scanf("%d%d",&v[i],&w[i]);
  12. for(int i=1;i<=n;i++)
  13. for(int j=0;j<=m;j++)
  14. if(j<v[i])
  15. dp[i][j]=dp[i-1][j]; //继承
  16. else
  17. dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]); //更新
  18. printf("%d",dp[n][m]);
  19. }

01背包一维数组优化

dp[i][j]能求得任意i,j情况下的最优解,但由于最终只需要求dp[n][m],所以只需要一维就可以维持

枚举背包容量需要逆序!

简单来说因为正序会使后面要用到的数据被覆盖,而倒序不会出现这个问题。

具体来讲我也忘了

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1010;
  4. int n,m;
  5. int v[N],w[N];
  6. int dp[N];
  7. int main()
  8. {
  9. scanf("%d%d",&n,&m);
  10. for(int i=1;i<=n;i++)
  11. scanf("%d%d",&v[i],&w[i]);
  12. for(int i=1;i<=n;i++)
  13. for(int j=m;j>=v[i];j--)
  14. dp[j]=max(dp[j],dp[j-v[i]]+w[i]); //倒序,防止覆盖
  15. printf("%d",dp[m]);
  16. }

完全背包(物品无限)

朴素完全背包

第一层循环——枚举物品

第二层循环——枚举容量

第三层循环——枚举个数        条件为k*v[i]<=j,超过容量即退出

 递推式  dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i])  前者表示维持当前最大值,后者表示取k个i物品

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1010;
  4. int n,m;
  5. int v[N],w[N];
  6. int dp[N][N];
  7. int main()
  8. {
  9. scanf("%d%d",&n,&m);
  10. for(int i=1;i<=n;i++)
  11. scanf("%d%d",&v[i],&w[i]);
  12. for(int i=1;i<=n;i++)
  13. for(int j=0;j<=m;j++)
  14. for(int k=0;k*v[i]<=j;k++)
  15. dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
  16. printf("%d",dp[n][m]);
  17. }

完全背包优化

优化思路

  1. f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w , f[i-1,j-2*v]+2*w , f[i-1,j-3*v]+3*w , .....)
  2. f[i , j-v]= max( f[i-1,j-v] , f[i-1,j-2*v] + w , f[i-1,j-3*v]+2*w , .....)
  3. 由上两式,可得出如下递推关系:
  4. f[i][j]=max(f[i,j-v]+w , f[i-1][j])
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1010;
  4. int n,m;
  5. int v[N],w[N];
  6. int dp[N][N];
  7. int main()
  8. {
  9. scanf("%d%d",&n,&m);
  10. for(int i=1;i<=n;i++)
  11. scanf("%d%d",&v[i],&w[i]);
  12. for(int i=1;i<=n;i++)
  13. {
  14. for(int j=0;j<=m;j++) //从小到大枚举,与01背包不同
  15. {
  16. dp[i][j]=dp[i-1][j];
  17. if(j>=v[i])
  18. dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);
  19. }
  20. }
  21. printf("%d",dp[n][m]);
  22. }

完全背包一维优化

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1010;
  4. int n,m;
  5. int v[N],w[N];
  6. int dp[N];
  7. int main()
  8. {
  9. scanf("%d%d",&n,&m);
  10. for(int i=1;i<=n;i++)
  11. scanf("%d%d",&v[i],&w[i]);
  12. for(int i=1;i<=n;i++)
  13. for(int j=v[i];j<=m;j++)
  14. dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
  15. printf("%d",dp[m]);
  16. }

多重背包(物品有限)

01背包是特殊的多重背包

代码与01背包雷同

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=110;
  4. int n,m;
  5. int v[N],w[N],s[N];
  6. int dp[N];
  7. int main()
  8. {
  9. scanf("%d%d",&n,&m);
  10. for(int i=1;i<=n;i++)
  11. scanf("%d%d%d",&v[i],&w[i],&s[i]);
  12. for(int i=1;i<=n;i++)
  13. for(int j=m;j>=v[i];j--)
  14. for(int k=0;k<=s[i]&&k*v[i]<=j;k++) //01背包的k相当于恒为1
  15. dp[j]=max(dp[j-k*v[i]]+k*w[i],dp[j]);
  16. printf("%d",dp[m]);
  17. }

区间dp

求区间最优解,将区间分隔为更小的区间,再由小区间最优解得到大区间最优解

模板

  1. for(int len=1;len<=n;len++) //先枚举长度
  2. {
  3. for(int i=1;i+len-1<=n;i++) //枚举起点
  4. {
  5. int j=i+len-1; //由起点和长度得出终点
  6. for(int k=i;k+1<=j;k++)
  7. dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+______);
  8. }
  9. }

线性

石子合并

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=310;
  4. int a[N],b[N],dp[N][N];
  5. int n;
  6. int main()
  7. {
  8. scanf("%d",&n);
  9. for(int i=1;i<=n;i++)
  10. {
  11. scanf("%d",&a[i]);
  12. b[i]=b[i-1]+a[i];//记录前缀和
  13. }
  14. memset(dp,0x3f3f3f3f,sizeof(dp));//初始化
  15. for(int len=1;len<=n;len++)
  16. {
  17. for(int i=1;i+len-1<=n;i++)
  18. {
  19. int j=len+i-1;
  20. if(len==1)
  21. {
  22. dp[i][j]=0;//最小区间
  23. continue;
  24. }
  25. for(int k=i;k<=j-1;k++)
  26. dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+b[j]-b[i-1]);
  27. }
  28. }
  29. printf("%d",dp[1][n]);
  30. }

环状

思路:将链打开

只需要将前n-1个数复制到后面

如123456  ->  12345612345

最后求max(dp(1,n),dp(2,n+1),......,dp(n,2n-1))

题目:环形石子合并

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=210;
  4. int n;
  5. int dpmax[2*N][2*N],dpmin[2*N][2*N],a[2*N];
  6. int b[2*N];
  7. int main()
  8. {
  9. scanf("%d",&n);
  10. for(int i=1;i<=n;i++)
  11. {
  12. scanf("%d",&a[i]);
  13. a[i+n]=a[i];
  14. }
  15. for(int i=1;i<=2*n;i++)
  16. b[i]=b[i-1]+a[i];
  17. memset(dpmin,0x3f3f3f,sizeof(dpmin));
  18. memset(dpmax,0,sizeof(dpmax)); //注意预处理!!!!
  19. for(int len=1;len<=n;len++)
  20. {
  21. for(int i=1;i+len-1<=2*n;i++)
  22. {
  23. int j=i+len-1;
  24. if(len==1)
  25. {
  26. dpmax[i][i]=0;
  27. dpmin[i][i]=0;
  28. continue;
  29. }
  30. for(int k=i;k+1<=j;k++)
  31. {
  32. dpmin[i][j]=min(dpmin[i][j],dpmin[i][k]+dpmin[k+1][j]+b[j]-b[i-1]);
  33. dpmax[i][j]=max(dpmax[i][j],dpmax[i][k]+dpmax[k+1][j]+b[j]-b[i-1]);
  34. }
  35. }
  36. }
  37. int maxn=0,minn=0x3f3f3f;
  38. for(int i=1;i<=n;i++)
  39. maxn=max(dpmax[i][i+n-1],maxn),minn=min(dpmin[i][i+n-1],minn);
  40. printf("%d\n%d",minn,maxn);
  41. }

计数dp

AcWing 900. 整数划分
题意:一个正整数 n 可以表示成若干个正整数之和,形如:n=n1+n2+…+nk,其中 n1≥n2≥…≥nk,k≥1。有多少种表示方法。

 

可转化为完全背包

有容量为1~n的n种物品,使背包容量恰好为n,问有多少种放法

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1010;
  4. const int MOD=1e9+7;
  5. int n;
  6. int dp[N];
  7. int main()
  8. {
  9. scanf("%d",&n);
  10. dp[0]=1; //0有一种放法,后面的状态全由0转移出来
  11. for(int i=1;i<=n;i++)
  12. for(int j=i;j<=n;j++)
  13. {
  14. dp[j]+=dp[j-i];
  15. dp[j]%=MOD;
  16. }
  17. printf("%d",dp[n]);
  18. }

Acwing 1021. 货币系统

给你一个n种面值的货币系统,求组成面值为m的货币有多少种方案。

输入格式

第一行,包含两个整数n和m。

接下来n行,每行包含一个整数,表示一种货币的面值。

输出格式

共一行,包含一个整数,表示方案数。

数据范围

n≤15,m≤3000

输入样例:

  1. 3 10
  2. 1
  3. 2
  4. 5

输出样例:

10
  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. using namespace std;
  4. const int N=20;
  5. const int M=3010;
  6. ll n,m;
  7. ll dp[M],a[N];
  8. int main()
  9. {
  10. scanf("%lld%lld",&n,&m);
  11. for(int i=1;i<=n;i++)
  12. scanf("%lld",&a[i]);
  13. dp[0]=1;
  14. for(int i=1;i<=n;i++)
  15. for(int j=a[i];j<=m;j++)
  16. dp[j]+=dp[j-a[i]];
  17. printf("%lld",dp[m]);
  18. }

线性dp

 

 

最长上升子序列

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1010;
  4. int n;
  5. int a[N],dp[N];
  6. int main()
  7. {
  8. scanf("%d",&n);
  9. for(int i=1;i<=n;i++)
  10. scanf("%d",&a[i]);
  11. for(int i=1;i<=n;i++)
  12. {
  13. dp[i]=1;
  14. for(int j=1;j<=i;j++)
  15. if(a[j]<a[i])
  16. dp[i]=max(dp[i],dp[j]+1);
  17. }
  18. int res=0;
  19. for(int i=1;i<=n;i++)
  20. res=max(res,dp[i]);
  21. printf("%d",res);
  22. }

最短编辑距离

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1010;
  4. int n,m;
  5. string a,b;
  6. int dp[N][N];
  7. int main()
  8. {
  9. scanf("%d",&n);
  10. cin>>a;
  11. scanf("%d",&m);
  12. cin>>b;
  13. memset(dp,0x3f3f3f3f,sizeof(dp)); //求最小值要初始化!!!!
  14. for(int i=1;i<=n;i++) //初始化
  15. dp[i][0]=i;
  16. for(int i=0;i<=m;i++) //初始化
  17. dp[0][i]=i;
  18. for(int i=0;i<n;i++)
  19. for(int j=0;j<m;j++)
  20. {
  21. int x=i+1,y=j+1;
  22. dp[x][y]=min(dp[x-1][y-1],min(dp[x-1][y],dp[x][y-1]))+1;
  23. if(a[i]==b[j])
  24. dp[x][y]=min(dp[x][y],dp[x-1][y-1]);
  25. }
  26. printf("%d",dp[n][m]);
  27. }

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/465102
推荐阅读
相关标签
  

闽ICP备14008679号