3.17~3.28背包问题
2018-03-28 13:09
183 查看
01背包
题库P5#416采药i循环为阶段,即当前考虑第i个物品取不取
j循环为状态,即当前背包容量还剩j
f[i][j]表示已经考虑了前i个物品,背包已装了容量为j的物品时的最大价值
Q:这里j为什么是倒序循环?
A:用反证法——如果j正序循环,那么当更新f[j]时会发现f[j-t[i]]可能已经被更新过。而如果f[j-t[i]]已被更新过的话说明第i个物品已经取了1次,此时再要更新f[j]的话会导致第i个物品又取了1次,这就与01背包每个物品只取1次相违背。因此需要倒序循环。
#include<bits/stdc++.h> using namespace std; long long T,m,t[110],v[110],f[1010]; inline int read() { int num=0,flag=1; char c=getchar(); for (;c<'0'||c>'9';c=getchar()) if (c=='-') flag=-1; for (;c>='0'&&c<='9';c=getchar()) num=(num<<3)+(num<<1)+c-48; return num*flag; } int main() { T=read(); m=read(); for (int i=1;i<=m;++i) { t[i]=read(); v[i]=read(); } for (int i=1;i<=m;++i) for (int j=T;j>=t[i];--j) f[j]=max(f[j],f[j-t[i]]+v[i]); printf("%lld\n",f[T]); return 0; }
完全背包
题库P5#418完全背包模版同样地,i为阶段,j为状态,f[i][j]的意义也一样
但这里,j是正序循环。这是因为一个物品可以取多次,f[j]可以在f[j-t[i]]被更新的基础上再次被更新,这样就保证了第i个物品可以取多次。
#include<bits/stdc++.h> using namespace std; long long m,n,v[110],w[110],f[310]; inline int read() { int num=0,flag=1; char c=getchar(); for (;c<'0'||c>'9';c=getchar()) if (c=='-') flag=-1; for (;c>='0'&&c<='9';c=getchar()) num=(num<<3)+(num<<1)+c-48; return num*flag; } int main() { m=read(); n=read(); for (int i=1;i<=n;++i) { w[i]=read(); v[i]=read(); } for (int i=1;i<=n;++i) for (int j=w[i];j<=m;++j) f[j]=max(f[j],f[j-w[i]]+v[i]); printf("max=%lld\n",f[m]); return 0; }
多重背包
题库P5#420逃亡的准备多重背包最终目的是把它转换成01背包问题。那它是怎么做到的呢?
这里采用二进制拆分法——一个物品有mm个,那么把mm拆成20+21+2<
10258
/span>2+…+mm′20+21+22+…+mm′,mm’值为mm−20−21−22−…mm−20−21−22−…,减到不能再减为止,剩下的值就是它(不为负数)。那么我们就可以用这里的数凑成所有<=mm的数。因此这就转换成了01背包问题。
#include<bits/stdc++.h> using namespace std; long long n,v,mm,ww,ss,w[10000010],s[10000010],tot,f[5010]; inline int read() { int num=0,flag=1; char c=getchar(); for (;c<'0'||c>'9';c=getchar()) if (c=='-') flag=-1; for (;c>='0'&&c<='9';c=getchar()) num=(num<<3)+(num<<1)+c-48; return num*flag; } int main() { n=read(); v=read(); for (int i=1;i<=n;++i) { mm=read(); ww=read(); ss=read(); int j=1; while (1) { if (mm>j) { tot++; mm-=j; w[tot]=ww*j; s[tot]=ss*j; } else { tot++; w[tot]=ww*mm; s[tot]=ss*mm; break; } j*=2; } } for (int i=1;i<=tot;++i) for (int j=v;j>=w[i];--j) f[j]=max(f[j],f[j-w[i]]+s[i]); printf("%lld\n",f[v]); return 0; }
混合三种背包
暂时没有题目友情链接:https://blog.csdn.net/qq_39670434/article/details/79475683
二维费用的背包
暂时没有题目友情链接:https://blog.csdn.net/qq_39670434/article/details/79476427
分组背包
题库P5#422竞赛真理因为每一组的物品间有冲突,每一组里最多只能取一个物品,那么在每一组里面采取01背包的思想,在每一组里考虑该物品取不取。
#include<bits/stdc++.h> using namespace std; long long n,t,w1[50],w2[50],t1[50],t2[50],f[1080010]; inline int read() { int num=0,flag=1; char c=getchar(); for (;c<'0'||c>'9';c=getchar()) if (c=='-') flag=-1; for (;c>='0'&&c<='9';c=getchar()) num=(num<<3)+(num<<1)+c-48; return num*flag; } int main() { n=read(); t=read(); for (int i=1;i<=n;++i) { w1[i]=read(); t1[i]=read(); w2[i]=read(); t2[i]=read(); } for (int i=1;i<=n;++i) for (int j=t;j>=min(t1[i],t2[i]);--j) { if (j>=t1[i]) f[j]=max(f[j],f[j-t1[i]]+w1[i]); if (j>=t2[i]) f[j]=max(f[j],f[j-t2[i]]+w2[i]); } printf("%lld\n",f[t]); return 0; }
有依赖的背包问题
题库P5#423金明的预算方案有依赖的背包是加强版的分组背包问题,只不过中间加了一个特判,一组物品中可能可以取多个而已。因为是取了主件才可以取附件,因此一定要加这个if判断。而可以省略else的原因是——在if语句里的第一句话已经取了一个max,如果不取主件,那么f[j]还是f[j];如果要取主件,f[j]是会被更新的。
#include<bits/stdc++.h> using namespace std; long long n,m,vv,pp,qq,v[100][5],p[100][5],f[32010]; inline int read() { int num=0,flag=1; char c=getchar(); for (;c<'0'||c>'9';c=getchar()) if (c=='-') flag=-1; for (;c>='0'&&c<='9';c=getchar()) num=(num<<3)+(num<<1)+c-48; return num*flag; } int main() { n=read(); m=read(); for (int i=1;i<=m;++i) { vv=read(); pp=read(); qq=read(); if (qq==0) { v[i][0]=vv; p[i][0]=pp; } else { if (v[qq][1]==0) { v[qq][1]=vv; p[qq][1]=pp; } else { v[qq][2]=vv; p[qq][2]=pp; } } } for (int i=1;i<=m;++i) for (int j=n;j>=0;--j) if (j>=v[i][0]) { f[j]=max(f[j],f[j-v[i][0]]+v[i][0]*p[i][0]); if (j>=v[i][0]+v[i][1]) f[j]=max(f[j],f[j-v[i][0]-v[i][1]]+v[i][0]*p[i][0]+v[i][1]*p[i][1]); if (j>=v[i][0]+v[i][2]) f[j]=max(f[j],f[j-v[i][0]-v[i][2]]+v[i][0]*p[i][0]+v[i][2]*p[i][2]); if (j>=v[i][0]+v[i][1]+v[i][2]) f[j]=max(f[j],f[j-v[i][0]-v[i][1]-v[i][2]]+v[i][0]*p[i][0]+v[i][1]*p[i][1]+v[i][2]*p[i][2]); } printf("%lld\n",f ); return 0; }
泛化物品背包
暂时没有题目友情链接:https://blog.csdn.net/qq_39670434/article/details/79483102
背包问题的变化
呵,变化可真多!T1
题库P5#417集合求和这是01背包的求方案总数,把max改成求和
#include<bits/stdc++.h> using namespace std; long long n,sum,a[50],f[1000010]={1}; inline int read() { int num=0,flag=1; char c=getchar(); for (;c<'0'||c>'9';c=getchar()) if (c=='-') flag=-1; for (;c>='0'&&c<='9';c=getchar()) num=(num<<3)+(num<<1)+c-48; return num*flag; } int main() { n=read(); sum=(n+1)*n/2; if (sum%2==1) { printf("0\n"); return 0; } for (int i=1;i<=n;++i) a[i]=i; for (int i=1;i<=n;++i) for (int j=sum/2;j>=a[i];--j) f[j]=f[j]+f[j-a[i]]; printf("%lld\n",f[sum/2]/2); return 0; }
T2
题库P5#419质数和分解这是完全背包的求方案总数,同样地,求和
#include<bits/stdc++.h> using namespace std; long long n,a[5010],temp,f[1000010]={1}; inline int read() { int num=0,flag=1; char c=getchar(); for (;c<'0'||c>'9';c=getchar()) if (c=='-') flag=-1; for (;c>='0'&&c<='9';c=getchar()) num=(num<<3)+(num<<1)+c-48; return num*flag; } bool panduan(int t) { for (int i=2;i<=sqrt(t);++i) if (t%i==0) return false; return true; } int main() { n=read(); for (int i=2;i<=n;++i) if (panduan(i)) a[++temp]=i; for (int i=1;i<=temp;++i) for (int j=a[i];j<=n;++j) f[j]=f[j]+f[j-a[i]]; printf("%lld\n",f ); return 0; }
T3
题库P5#421潜水员的规划因为它要计算为了完成他的工作需要的气缸的重量的最低值,首先要保证能完成他的工作,这就要求f[][]的下标>=T和A,所以i和j循环枚举的时候要适当扩大范围,然后把max改成min
#include<bits/stdc++.h> using namespace std; long long T,A,n,t[1010],a[1010],w[1010],f[1000][1000],ans; inline int read() { int num=0,flag=1; char c=getchar(); for (;c<'0'||c>'9';c=getchar()) if (c=='-') flag=-1; for (;c>='0'&&c<='9';c=getchar()) num=(num<<3)+(num<<1)+c-48; return num*flag; } int main() { T=read(); A=read(); n=read(); for (int i=1;i<=n;++i) { t[i]=read(); a[i]=read(); w[i]=read(); } memset(f,10,sizeof(f)); ans=f[0][0]; f[0][0]=0; for (int i=1;i<=n;++i) for (int j=T+100;j>=t[i];--j) for (int k=A+100;k>=a[i];--k) f[j][k]=min(f[j][k],f[j-t[i]][k-a[i]]+w[i]); for (int i=T;i<=T+100;++i) for (int j=A;j<=A+100;++j) ans=min(ans,f[i][j]); printf("%lld\n",ans); return 0; }
T4
题库P5#425背包的第k优解这是求前k优解之和,大佬采用的都是用两个标记外加一个数组,逐层更新。这里f[j][k]表示的就是在背包体积为j时的第k优解的价值。
#include<bits/stdc++.h> using namespace std; long long K,V,n,t[210],v[210],a1,a2,temp,a[100],f[5010][100],ans; inline int read() { int num=0,flag=1; char c=getchar(); for (;c<'0'||c>'9';c=getchar()) if (c=='-') flag=-1; for (;c>='0'&&c<='9';c=getchar()) num=(num<<3)+(num<<1)+c-48; return num*flag; } int main() { K=read(); V=read(); n=read(); for (int i=1;i<=n;++i) { t[i]=read(); v[i]=read(); } memset(f,128,sizeof(f)); f[0][1]=0; for (int i=1;i<=n;++i) for (int j=V;j>=t[i];--j) { a1=1; a2=1; temp=0; while (temp<K) { if (f[j][a1]>=f[j-t[i]][a2]+v[i]) { a[++temp]=f[j][a1]; a1++; } else { a[++temp]=f[j-t[i]][a2]+v[i]; a2++; } } for (int k=1;k<=K;++k) f[j][k]=a[k]; } for (int i=1;i<=K;++i) ans+=f[V][i]; printf("%lld\n",ans); return 0; }
坑
破锣乐队(还没做过相关文章推荐
- 0-1背包问题
- 0-1背包问题(几种算法解)
- 第七周作业1--背包问题
- 01背包问题
- 动态规划之0-1背包问题
- 一道可用背包问题解的华为编程题
- 简单DP之最少硬币问题(多重背包问…
- 贪心方法-带有限期和收益的单位时间的作业排序贪心算法和背包问题
- 九度OJ:题目1030:毕业bg(经典背包问题)
- 背包问题九讲:
- 题目1454:Piggy-Bank(完全背包问题)
- hdu 2602 背包问题之01背包
- 背包问题 入门 hdu 2602
- 01背包问题-POJ 2184 Cow Exhibition
- 01背包、完全背包、多重背包、混合三种背包问题
- 动态规划之背包问题-总结和拓展(二)
- 学习笔记-背包问题
- 【背包九讲】P01: 01背包问题
- 背包问题
- 0/1背包问题 - 分枝定界 优先队列