您的位置:首页 > 其它

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;
}


破锣乐队(还没做过
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: