背包
1.填满型01背包[1]
倒序枚举避开后效性
题目描述
caioj1055【题意】
有n根木棍(0≤n≤30),从中选若干根使得它们的 长度和s 最接近v(正整数,0≤v≤20000),且s<=v。
【输入格式】
一个整数v,一个整数n。接下来n个整数,分别表示这n根木棍的长度。
【输出格式】
一个整数,表示v-s。
【样例输入】
24
6
8 3 12 7 9 7
【样例输出】
0
#include<bits/stdc++.h> using namespace std; int a[350],v,n; bool f[20000]; int main() { cin>>v>>n; for(int i=1;i<=n;i++)cin>>a[i]; f[0]=1; for(int i=1;i<=n;i++) for(int j=v;j>=a[i];j--) if(!f[j])f[j]=f[j-a[i]]; int x=v; while(!f[x])x--; cout<<v-x; return 0; }
2.填满型01背包 [2]
搞了一个小时才弄出来的东西…emmm和上一个差不多
caioj1056【题意】
有n个数列,每个数列各自选若干个数,使得每个数列的和一样大,并且这个和要尽量大。
【输入格式】
第一行是一个整数N(N<=100),表示一共有n个数列。
以下N行每行是一个系列非负整数,表示每个数列的数字,用-1结束。
一个数列中的数字个数不超过100个,每个数也不超过100。
【输出格式】
一个整数,表示使得每个数列的和一样大,并且这个和要尽量大的值。如果找不到合适的方案,则输出0。
【样例输入】
2
2 1 -1
3 2 1 -1
【样例输出】
3
#include<bits/stdc++.h> using namespace std; const int N = 1100; int n,x,maxn,a[N],tot[N]; bool f[N]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++){ int len=0; while(scanf("%d",&x)!=EOF){ if(x==-1)break; a[++len]=x; } maxn=0; //千万不要忘记清0! f[0]=1; //以下是核心部分!也是倒序看看能更新哪些数 for(int j=1;j<=len;j++) for(int k=maxn;k>=0;k--){ //核心·卡了一个小时的k循环!=。= if(f[k]){ f[k+a[j]]=1; maxn=max(maxn,k+a[j]); } } //这个循环应该可以优化…… for(int z=maxn;z>=0;z--){ if(f[z])tot[z]++; } } for(int i=maxn;i>=0;i--){ if(tot[i]==n){ cout<<i; break; } } }
3.含价值的填满型01背包
题目和分析都在下面啦
caioj1057【题意】
从前有个山洞里,洞有n个宝石,第i个宝石拿到市场能卖m[i]块钱,但是要拿走第i颗宝石需要花费t[i]秒的时间。
山洞V秒之后就会倒塌。
求在能活着离开山洞的前提下,获取的宝石总价值最大。
【输入文件】
第一行有两个整数V(1 <= V <= 1000)和n(1 <= n <= 100) 接下来的n行每行两个整数t[i] 和 m[i](范围0~100)
【输出文件】
输出一行,一个整数,即最大总价值。
【样例输入】 | 【样例输出】 |
70 3 71 100 69 1 1 2 | 3 |
16 5 5 4 8 9 7 5 3 4 6 9 | 18
|
【数据规模】
对于30%的数据,n<= 10;
对于全部的数据,n <= 100。
【分析】
二维数组,填表格方法。
以第二组数据为例。假设我们要的答案就是f[5,16],就是眼前有5颗宝石且你有16秒的时间,你能取得的最大的价值量。
假如第5颗宝石你不想取,那么f[5,16]=f[4,16]
假如第5颗宝石你想拿,那么 f[5,16]=f[4,10]+9
所以 f[5,16]=max( f[4,16] , f[4,10]+9)
16 5
5 4
8 9
7 5
3 4
6 9
具体实现如下表格:
f[i,j] | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 |
2 | 0 | 0 | 0 | 0 | 0 | 4 | 4 | 4 | 9 | 9 | 9 | 9 | 9 | 13 | 13 | 13 | 13 |
3 | 0 | 0 | 0 | 0 | 0 | 4 | 4 | 5 | 9 | 9 | 9 | 9 | 9 | 13 | 13 | 14 | 14 |
4 | 0 | 0 | 0 | 4 | 4 | 4 | 4 | 5 | 9 | 9 | 9 | 13 | 13 | 13 | 13 | 14 | 17 |
5 | 0 | 0 | 0 | 4 | 4 | 4 | 9 | 9 | 9 | 13 | 13 | 13 | 13 | 14 | 18 | 18 | 18 |
#include<bits/stdc++.h> using namespace std; int bg[150][10050],va[150],we[150]; int main() { int m,n; scanf("%d %d",&m,&n); for(int i=1;i<=n;i++) cin>>we[i]>>va[i]; for(int i=1;i<=n;i++){ for(int j=m;j>=0;j--){ if(we[i]<=j){ bg[i][j]=max(bg[i-1][j],bg[i-1][j-we[i]]+va[i]); //核心·状态转移方程 } else bg[i][j]=bg[i-1][j]; } } cout<<bg[n][m]; return 0; }
//为了和之后的背包题联系起来,这里再贴另一份(优化)代码
#include<bits/stdc++.h> using namespace std; const int N=110000,M=11000; int f[N],t[M],v[M]; int main() { int n,T; scanf("%d%d",&T,&n); for(int i=1;i<=n;i++)scanf("%d%d",&t[i],&v[i]); for(int i=1;i<=n;i++){ for(int j=T;j>=1;j--){ if(j>=t[i]){ f[j]=max(f[j],f[j-t[i]]+v[i]); } } } printf("%d",f[T]); return 0; }
4.含价值的填满型完全背包
【题意】
小明背着一个背包(最大能带的重量为V)走进一个山洞,
山洞里有n 种 宝石(每种宝石无限多个),第 i 种 宝石的重量为v[i],拿到宝石店能卖m[i]块钱。
求在背包能承受重量的范围内,使得小明装进背包的宝石总价值最大。
【输入文件】
第一行有两个整数V(1 <= V <= 1000)和n(1 <= n <= 100)
接下来的n行每行两个整数v[i] 和 m[i](范围0~100)
【输出文件】
输出一行,一个整数,即最大总价值。
【样例输入】
70 3
71 100
69 1
1 2
【样例输出】
140
【数据规模】
对于30%的数据,n <= 10;
对于全部的数据,n<= 100。
#include<bits/stdc++.h> using namespace std; const int N=110000,M=11000; int f[N],t[M],v[M]; int main() { int n,V; scanf("%d%d",&V,&n); for(int i=1;i<=n;i++)scanf("%d%d",&t[i],&v[i]); for(int i=1;i<=n;i++){ for(int j=1;j<=V;j++){ //只有这一句话变了(也就是只有查找的顺序变了/从前往后刷/尽量找占重量小的物品) if(j>=t[i]){ f[j]=max(f[j],f[j-t[i]]+v[i]); } } } printf("%d",f[V]); return 0; }
区间DP
刷题一时爽,一直刷题一直爽……
刷完这道题我就回来总结_(:3/_)__
子序列
- 最长不下降子序列
【例题1】
1 #include<iostream> #include<cstdio> using namespace std; int a[3500],f[3500]; int main() { int n,ans = 0; scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",a+i); for(int i = 1; i <= n; i++) for(int j = 0; j < i; j++) { if(a[j] <= a[i]) f[i] = max(f[i],f[j] + 1); ans=max(ans,f[i]); } printf("%d\n",ans); return 0; }
【例题2】IPUOJ10804
1 2 2 #include<bits/stdc++.h> 3 using namespace std; 4 const int N =3500; 5 int ans=INT_MIN; 6 int n,a[N],f[N][2]; 7 int main() 8 { 9 scanf("%d",&n); 10 for(int i =1; i <= n; i++)scanf("%d",&a[i]); 11 12 for(int i =1; i <= n; i++){ 13 for(int j =1; j < i; j++){ 14 if(a[i]>= a[j]){ 15 f[i][0]= max( f[i][0],f[j][0]+1); 16 f[i][1]= max( f[i][1],f[j][1]+1); 17 } 18 f[i][1]= max(f[i][1], f[j][0]+1); 19 } 20 ans = max(ans,max(f[i][1],f[i][0])); 21 } 22 printf("%d",ans +1); 23 }
【例题3】
#include<bits/stdc++.h> using namespace std; const int N=3500; int n,K,ans,a[N],f[N][15]; int main() { scanf("%d%d",&n,&K); for(int i = 1;i <= n;i++)scanf("%d", &a[i]); for(int i = 1; i <= n; i++){ for(int j = 0; j < i; j++){//j是可以从0开始的,但一定要 < i for(int k = 1; k <= K + 1; k++){ if(a[j] <= a[i]) f[i][k] = max(f[i][k], f[j][k] + 1); f[i][k] = max(f[i][k],f[j][k - 1] + 1); ans = max(ans,f[i][k]); } } } printf("%d",ans); }