当前位置:   article > 正文

蓝桥杯每日一题(背包dp,线性dp)

蓝桥杯每日一题(背包dp,线性dp)

//3382 整数拆分

将 1,2,4,8看成一个一个的物品,以完全背包的形式放入。

一维形式:f]0]=1;

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. //3382整数拆分
  4. const int N=1e6+10, M=5e5+10;
  5. int mod=1e9;
  6. int f[N],n;
  7. int main()
  8. {
  9. cin>>n;
  10. //转化为完全背包问题,
  11. //状态是找前i个二进制数,组成j的数量
  12. f[0]=1;
  13. for(int i=1;i<=n;i*=2)//
  14. //和完全背包一样遍历所有物品(以前i件物品)
  15. {
  16. for(int j=i;j<=n;j++)
  17. {
  18. f[j]=(f[j]+f[j-i])%mod;
  19. }
  20. }
  21. cout<<f[n]<<endl;
  22. }

由于二维形式会爆。下面给出写法

需要注意的一点是:由于每次只用到前一层循环的数据,当前层j放不下i的位置,也就是j<i的位置,也要更新,因为下一层可能用到这个数据,不更新的话,就都是0了。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1e3+10, M=5e2+10;
  4. int mod=1e9;
  5. int f[N][N], n;
  6. int main()
  7. {
  8. cin >> n;
  9. for(int i=0; i<=n; i++)
  10. {
  11. f[i][0] = 1;
  12. }
  13. int t;
  14. for(int i=1; i<=n; i*=2)
  15. {
  16. t=i;
  17. for(int j=1; j<=n; j++)
  18. {
  19. f[i][j] = f[i/2][j];
  20. if(j >= i)
  21. {
  22. f[i][j] = (f[i][j] + f[i][j-i]) % mod;
  23. }
  24. }
  25. }
  26. cout << f[t][n] << endl;
  27. }

  2 01 背包问题

注意存w和v的的值是从1开始。因为还要考虑状态i-1 

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. // 2 01 背包问题
  4. const int N=1010;
  5. int w[N],v[N];
  6. int f[N][N];//前N件物品,背包容量
  7. int main()
  8. {
  9. int n,V;
  10. cin>>n>>V;
  11. for(int i=1;i<=n;i++)
  12. {
  13. cin>>v[i]>>w[i];
  14. }
  15. //for(int i=0;i<=V;i++)f[0][i]=0;
  16. for(int i=1;i<=n;i++)
  17. {
  18. for(int j=0;j<=V;j++)
  19. {
  20. f[i][j]=f[i-1][j];
  21. if(j>=v[i])f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
  22. }
  23. }
  24. cout<<f[n][V]<<endl;
  25. }

8完全背包问题

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. // 8 完全背包问题
  4. const int N=1010;
  5. int f[N][N];
  6. int n,m;
  7. int w[N],v[N];
  8. int main()
  9. {
  10. cin>>n>>m;
  11. for(int i=1;i<=n;i++)
  12. {
  13. cin>>v[i]>>w[i];
  14. }
  15. for(int i=1;i<=n;i++)
  16. {
  17. for(int j=0;j<=m;j++)
  18. {
  19. f[i][j]=f[i-1][j];
  20. if(j>=v[i])f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);
  21. }
  22. }
  23. cout<<f[n][m]<<endl;
  24. }

9分组背包问题

完全背包问题是k的个数作为循环。分组背包是组内的所有物品作为循环;

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. //9 分组背包问题
  4. const int N=110;
  5. int f[N][N];
  6. int n,m;
  7. int w[N][N],v[N][N],s[N];
  8. int main()
  9. {
  10. cin>>n>>m;
  11. for(int i=1;i<=n;i++)
  12. {
  13. int t;
  14. cin>>t;
  15. s[i]=t;
  16. for(int j=1;j<=t;j++)
  17. {
  18. cin>>v[i][j]>>w[i][j];
  19. }
  20. }
  21. for(int i=1;i<=n;i++)
  22. {
  23. for(int j=0;j<=m;j++)
  24. {
  25. f[i][j]=f[i-1][j];
  26. for(int k=1;k<=s[i];k++)
  27. {
  28. if(v[i][k]<=j)
  29. {
  30. f[i][j]=max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);
  31. }
  32. }
  33. }
  34. }
  35. cout<<f[n][m]<<endl;
  36. }

//6 多重背包问题III

多重背包比完全背包多了个数的限制条件

没有用优化:SF错误

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. //6 多重背包问题 III
  4. const int N=3010;
  5. int f[N];
  6. int n,m;
  7. int w[N],v[N],s[N];
  8. int main()
  9. {
  10. cin>>n>>m;
  11. for(int i=1;i<=n;i++)
  12. {
  13. cin>>v[i]>>w[i]>>s[i];
  14. }
  15. for(int i=1;i<=n;i++)
  16. {
  17. for(int j=0;j<=m;j++)
  18. {
  19. for(int k=0;k <= s[i] && k*v[i] <= j;k++)
  20. {
  21. f[j]=max(f[j],f[j-v[i]*k]+w[i]*k);
  22. }
  23. }
  24. }
  25. cout<<f[m]<<endl;
  26. }

//1051最大的和

f数组保存从1到i,连续的子串的最大值。

rf数组保存从i到n的~

在计算f的时候,要时刻记住求的是前i个数中连续的一个最大的子串

然后用df思想思考,要么选a[i]要么不选,选ai的时候要保证前面是连续的。

因而用s这个临时变量,保存必选ai的时候的最大值。‘

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. //1051最大的和
  4. const int N=50010;
  5. int f[N],rf[N],a[N];
  6. int main()
  7. {
  8. int t,n;
  9. cin>>t;
  10. while(t--)
  11. {
  12. cin>>n;
  13. for(int i=1; i<=n; i++)cin>>a[i];
  14. //避免出现全都是负数,但是结果为全0
  15. memset(f,-0x3f,sizeof f);
  16. memset(rf,-0x3f,sizeof rf);
  17. //找从1到n 以i结尾的连续串的最大的和是多少
  18. int s=0;//截止到i这个位置,而且必须加上a[i]的最大值
  19. for(int i=1; i<=n; i++)
  20. {
  21. //用闫氏dp的思考方式思考
  22. //i这个点的f值,要么加上a[i],要么不加。
  23. //s就保存了加上ai的结果,s永远保存在运行到i的时候的位置的最大值(包含a[i])
  24. s=max(s,0)+a[i];//要保证是一个和a[i]连续的数
  25. f[i]=max(s,f[i-1]);
  26. }
  27. s=0;
  28. for(int i=n; i>=1; i--)
  29. {
  30. s=max(0,s)+a[i];
  31. rf[i]=max(s,rf[i+1]);
  32. }
  33. int res=-0x3f3f3f3f;
  34. for(int i=1;i<=n;i++)
  35. {
  36. res=max(res,rf[i+1]+f[i]);
  37. }
  38. cout<<res<<endl;
  39. }
  40. }

898、数字三角形

第一个自己做出来的线性dp,注意为了防止出现全零的情况,一定要初始化为-0x3f3f3f3f

在找上一个状态的时候,注意下标不要错。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. //898 数字三角形
  4. const int N=510;
  5. int f[N][N];
  6. int a[N][N];
  7. int main()
  8. {
  9. int n;
  10. cin>>n;
  11. for(int i=1;i<=n;i++)
  12. {
  13. for(int j=1;j<=i;j++)
  14. {
  15. cin>>a[i][j];
  16. }
  17. }
  18. for(int i=0;i<=n;i++)
  19. {
  20. for(int j=0;j<=i;j++)
  21. {
  22. f[i][j]=-0x3f3f3f3f;
  23. }
  24. }
  25. f[1][1]=a[1][1];
  26. for(int i=2;i<=n;i++)
  27. {
  28. for(int j=1;j<=i;j++)
  29. {
  30. if(j==1)
  31. {
  32. f[i][j]=f[i-1][1]+a[i][j];
  33. }
  34. else if(j==i)
  35. {
  36. f[i][j]=f[i-1][i-1]+a[i][j];
  37. }
  38. else
  39. {
  40. f[i][j]=max(f[i-1][j-1]+a[i][j],f[i-1][j]+a[i][j]);
  41. }
  42. }
  43. }
  44. int res=-0x3f3f3f3f;
  45. for(int i=1;i<=n;i++)
  46. {
  47. res=max(res,f[n][i]);
  48. }
  49. cout<<res<<endl;
  50. }

//895、最长上升子序列

没有严格按照岩石地皮分析法思考导致没想出来。

数组表示某个点之前的点,构成的子序列的最长长度。

状态的计算:循环之前的所有以小于i的点为结尾的子序列的长度,然后根据大小是否加一,是否更新。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. //895 最长上升子序列
  4. const int N=1010;
  5. int n;
  6. int f[N],a[N];
  7. int main()
  8. {
  9. cin >> n;
  10. for(int i=1;i<=n;i++)cin>>a[i];
  11. memset(f,0x3f,sizeof f);
  12. //memset(a,0x3f,sizeof a);
  13. f[1]=1;
  14. for(int i=2;i<=n;i++)
  15. {
  16. f[i]=1;
  17. for(int j=1;j<=i;j++)
  18. {
  19. if(a[i]>a[j])
  20. {
  21. if(f[j]+1>f[i])
  22. {
  23. f[i]=f[j]+1;
  24. }
  25. }
  26. }
  27. }
  28. cout<<f[n]<<endl;
  29. }

897 最长公共子序列

状态计算看的题解,如果相等就是f[i-1][j-1]+1

如果不等,其中至少有一个不再公共子序列里面。

max(f[i-1][j],f[i][j-1],f[i-1][j-1]) 根据常识知道f[i-1][j-1]<=(f[i-1][j],f[i][j-1]的任意一个)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. //897 最长公共子序列
  4. const int N=1010;
  5. int n,m;
  6. int f[N][N];
  7. char a[N],b[N];
  8. int main()
  9. {
  10. cin >> n>>m;
  11. scanf("%s",a+1);
  12. scanf("%s",b+1);
  13. for(int i=1; i<=n; i++)
  14. {
  15. for(int j=1; j<=m; j++)
  16. {
  17. if(a[i]==b[j])
  18. {
  19. f[i][j]=f[i-1][j-1]+1;
  20. }
  21. else
  22. {
  23. f[i][j]=max(f[i-1][j],f[i][j-1]);
  24. }
  25. }
  26. }
  27. cout<<f[n][m]<<endl;
  28. }

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

闽ICP备14008679号