各种背包乱搞
01背包
#include<iostream> using namespace int f[1005];//体积小于等于i的情况下最大价值是多少 int main() { int n,m; cin >> n >> m; for(int i=1;i<=n;i++) { int v,w; cin >> v >> w; for(int j=m;j>=v;j--)//从大到小是因为没有重复计算 { //从小到大算的话f[i-v]这个状态之前被算过了 f[j]=max(f[j],f[j-v]+w); } } cout<<f[m]<<endl; return 0; }
完全背包
#include<iostream> using namespace std; int f[1005];//体积小于等于i的情况下最大价值是多少 int main() { int n,m; cin >> n >> m; for(int i=1;i<=n;i++) { int v,w; cin >> v >> w; for(int j=v;j<=m;j++)//因为物品可以取无限次,那么从小到大 { //枚举 正好可以利用之前重复算过的 f[j]=max(f[j],f[j-v]+w); } } cout<<f[m]<<endl; return 0; }
二维费用(体积,重量)
/*做法类比01背包,加上枚举重量的循环就可以了*/ #include<iostream> using namespace std; int f[1005][1005];//体积小于等于i,重量小于等于j的情况下最大价值是多少 int main() { int n,m,t; cin >> n >> m >> t; for(int i=1;i<=n;i++) { int v,s,w; cin >> v >> s >>w; for(int j=m ;j>=v;j--) { for(int k=t;k>=s;k--) { f[j][k]=max(f[j][k],f[j-v][k-s]+w); } } } cout<<f[m][t]<<endl; return 0; }
分组背包
#include<iostream> using namespace std; int f[1005];//表示前i组物品体积不超过j的情况下的最大价值 int v[105]; int w[105]; int main() { int n,m; cin >> n>> m; for(int i=1;i<=n;i++) { int s; cin >> s; for(int j=1;j<=s;j++) cin>> v[j]>> w[j]; for(int j=m;j>=0;j--) { for(int k=1;k<=s;k++) { if(j>=v[k]) { f[j]=max(f[j],f[j-v[k]]+w[k]); } } } } cout<<f[m]<<endl; return 0; }
多重背包
1.枚举num
for(int i=1;i<=n;i++)//枚举物品 for(int j=V;j>=0;j--)//枚举体积 for(int k=1;k<=num[i],k++) //这个枚举到num[i]更省心 if(j-k*c[i]>=0)//判断能否装下. f[j]=max(f[j],f[j-k*c[i]]+k*w[i]);
2.二进制拆分
用$1,2,4...2^n$可以表示出$2^{n+1}-1$以内所有的正整数
那么对1到$num[i]$进行拆分 得到多件大物品($1,2,4,8...$件物品的和)
之后跑01背包
for(int i=1;i<=n;i++) { for(int j=1;j<=num[i];j<<=1) //二进制每一位枚举. //注意要从小到大拆分 { num[i]-=j;//减去拆分出来的 new_c[++tot]=j*c[i];//合成一个大的物品的体积 new_w[tot]=j*w[i];//合成一个大的物品的价值 } if(num[i])//判断是否会有余下的部分. //就好像我们某一件物品为13,显然拆成二进制为1,2,4. //我们余出来的部分为6,所以需要再来一份. { new_c[++tot]=num[i]*c[i]; new_w[tot]=num[i]*w[i]; num[i]=0; } }
单调队列优化
for(int i=1;i<=n;i++)//枚举物品种类 { cin>>c[i]>>w[i]>>num[i];//c,w,num分别对应 体积,价值,个数 if(V/c[i] <num[i]) num[i]=V/c[i];//求lim for(int mo=0;mo<c[i];mo++)//枚举余数 { head=tail=0;//队列初始化 for(int k=0;k<=(V-mo)/c[i];k++) { int x=k; int y=f[k*c[i]+mo]-k*w[i]; while(head<tail && que[head].pos<k-num)head++;//限制长度 while(head<tail && que[tail-1].value<=y)tail--; que[tail].value=y,que[tail].pos=x; tail++; f[k*c[i]+mo]=que[head].value+k*w[i]; //加上k*w[i]的原因: //我们的单调队列维护的是前i-1种的状态最大值. //因此这里加上k*w[i]. } } }
复杂度$O(n*V)$
有依赖的背包
多级附件:
建树存先根遍历preo[],子树大小size[]
设w[]为代价,v[]为价值
先用一个虚点作为超级祖先
转移时 对于某个节点,如果购买它,考虑它的子节点
如果不购买,跳过它的子树
for(int i=scc;i>=1;i--) { int node=preo[i]; for(int j=m;j>=0;j--) { if(j<w[node])dp[i][j]=dp[i+siz[node]][j]; else dp[i][j]=max(dp[i+1][j-w[node]]+v[node],dp[i+siz[node]][j]); } }
兴许青竹早凋,碧梧已僵,人事本难防。