赞
踩
前言:
蓝桥杯对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)朴素写法
- #include<iostream>
- using namespace std;
- const int N=1010;
- int n,m;
- int v[N],w[N];
- int f[N][N];
- int main()
- {
- cin>>n>>m;
- for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
- for(int i=1;i<=n;i++)
- {
- for(int j=0;j<=m;j++)
- {
- f[i][j]=f[i-1][j];
- if(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
- }
- }
- cout<<f[n][m]<<endl;
- return 0;
- }
(2)优化写法
- #include<iostream>
- using namespace std;
- const int N=1010;
- int n,m;
- int v[N],w[N];
- int f[N];
- int main()
- {
- cin>>n>>m;
- for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
- for(int i=1;i<=n;i++)
- {
- for(int j=m;j>=v[i];j++)
- {
- f[j]=max(f[j],f[j-v[i]]+w[i]);
- }
- }
- cout<<f[m]<<endl;
- return 0;
- }
- 路线问题
例题: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.代码
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N=110; int n,m; int w[N][N]; int f[N][N]; int main() { int t; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&w[i][j]); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) f[i][j]=max(f[i-1][j]+w[i][j],f[i][j-1]+w[i][j]); printf("%d\n",f[n][m]); } return 0; }
- 线性问题
例题: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.代码
#include<iostream> using namespace std; const int N=1010; int n; int a[N],f[N]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) { f[i]=1; for(int j=1;j<i;j++) { if(a[j]<a[i]) f[i]=max(f[i],f[j]+1); } } int res=0; for(int i=1;i<=n;i++) res=max(res,f[i]); printf("%d\n",res); return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。