当前位置:   article > 正文

蓝桥杯:简单DP&闫氏思考法_蓝桥杯考dp吗

蓝桥杯考dp吗

前言:

蓝桥杯对DP的考察还是比较多的,引起重视。

蓝桥杯最常见的三种形式:选择问题/组合问题(eg:背包问题 在众多选法中选择一个最优的选法)、路线问题(规定规则,按照这个规则走,找出最优的一条路线)、线性问题(一维的,例如最长上升子序列

有时候单独出现,经常是这三种形式的组合问题

建议:多做题,用过这个状态表示,考试时想出来这种状态表示概率才比较大

经验:

1.状态表示:第一维选择前i个物品

第二维一般是各种限制,体积、重量、选几个......

属性:Max/Min/数量

2.状态计算:集合划分为若干个子集,依据是找最后一个不同点

集合划分原则:不重不漏

3.优化:DP问题的所有优化都是看能不能对代码进行等价变形,换而言之优化后和问题本身没有关系,只和代码逻辑有关系

先用朴素的方法分析,在此基础上看能不能优化

  • 选择问题

例题:  acWing2. 01背包问题

1.状态表示:

集合 所有只考虑前i个物品总体积不超过j的选法的集合

属性 Max

f[i][j]

2.状态计算:

(1)所有选择第i个物品的方案 f[i-1][j]

(2)所有不选择第i个物品的方案 f[i-1][ j-v[i] ]+w[i]

f[i][j]=max(f[i-1][j],f[i-1][ j-v[i] ]+w[i])

3.优化:

滚动数组

4.代码

(1)朴素写法

  1. #include<iostream>
  2. using namespace std;
  3. const int N=1010;
  4. int n,m;
  5. int v[N],w[N];
  6. int f[N][N];
  7. int main()
  8. {
  9. cin>>n>>m;
  10. for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
  11. for(int i=1;i<=n;i++)
  12. {
  13. for(int j=0;j<=m;j++)
  14. {
  15. f[i][j]=f[i-1][j];
  16. if(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
  17. }
  18. }
  19. cout<<f[n][m]<<endl;
  20. return 0;
  21. }

(2)优化写法

  1. #include<iostream>
  2. using namespace std;
  3. const int N=1010;
  4. int n,m;
  5. int v[N],w[N];
  6. int f[N];
  7. int main()
  8. {
  9. cin>>n>>m;
  10. for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
  11. for(int i=1;i<=n;i++)
  12. {
  13. for(int j=m;j>=v[i];j++)
  14. {
  15. f[j]=max(f[j],f[j-v[i]]+w[i]);
  16. }
  17. }
  18. cout<<f[m]<<endl;
  19. return 0;
  20. }
  • 路线问题

例题:acWing1015. 摘花生

1.状态表示:

集合  所有从(1,1)走到(i,j)的路线

属性  Max

f[i][j]

2.状态计算

(1)最后一步是从上面下来  f[i-1][j]+w[i][j]

(2)最后一步是从左边过来的  f[i][j-1]+w[i][j]

f[i][j]=max(f[i-1][j]+w[i][j],f[i][j-1]+w[i][j])

3.代码

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<algorithm>
  4. using namespace std;
  5. const int N=110;
  6. int n,m;
  7. int w[N][N];
  8. int f[N][N];
  9. int main()
  10. {
  11. int t;
  12. scanf("%d",&t);
  13. while(t--)
  14. {
  15. scanf("%d%d",&n,&m);
  16. for(int i=1;i<=n;i++)
  17. for(int j=1;j<=m;j++)
  18. scanf("%d",&w[i][j]);
  19. for(int i=1;i<=n;i++)
  20. for(int j=1;j<=m;j++)
  21. f[i][j]=max(f[i-1][j]+w[i][j],f[i][j-1]+w[i][j]);
  22. printf("%d\n",f[n][m]);
  23. }
  24. return 0;
  25. }
  • 线性问题

例题:acWing 895. 最长上升子序列

1.状态表示:

集合  所有以a[i]结尾的严格单调上升子序列

属性  Max

f[i]

2.状态计算

最后一个不同点是倒数第二个数

(1)以a[1]结尾的上升子序列  f[1]+1

(2)以a[2]结尾的上升子序列  f[2]+1

...(i-1) 以a[i-1]结尾的上升子序列 f[i-1]+1

(i)空 1

f[i]=max( f[1]+1,f[2]+1,...,f[i-1]+1,1)

3.代码

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

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

闽ICP备14008679号