今天考了一天的试,五科放在一起考,差点累死我
emmm……感觉今天没有上课的话又要跟不上了(本身就是个蒟蒻现在更蒟蒻了)
不过博客还是要接着写(๑•̀ㅂ•́)و✧
【01背包问题】
问题:有n个物品和一个容量为m的背包,给出每件物品的体积v和价值w,每件物品只能选一次,求能获得的最大价值
定义:我们用f[i]表示体积为i的背包能获得的最大价值,那么最后的答案就是f[m]
状态转移方程:f[i]=max{f[i],f[i-v]+w} (i枚举的是背包体积)
理解:每放一个物品,我们都要给这个物品腾出一个v[i]的体积来存放(不然装不下),当前背包的价值加上物品的价值w[i],然后将这个值与原价值比较,较大的那个值就是此时背包能获得的最大价值
知道状态转移方程之后就很好写代码了,另外要注意的是枚举背包体积的时候要从后往前枚举,因为f[i]是从前面的状态转移过来的,先枚举后面的就可以防止一件物品被选取多次,就保证了这是01背包问题
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int M=205;
int f[M];
int main()
{
int m,n,i,j,v,w;
scanf("%d%d",&m,&n);
memset(f,0,sizeof(f));
for(i=1;i<=n;++i)
{
scanf("%d%d",&v,&w);
for(j=m;j>=v;--j)
f[j]=max(f[j],f[j-v]+w);
}
printf("%d",f[m]);
return 0;
}
【完全背包问题】
问题:有n个物品和一个容量为m的背包,给出每件物品的体积v[i]和价值w[i],每件物品可以选无数次,求能获得的最大价值
分析:我们观察问题,发现完全背包和01背包唯一的不同就是完全背包中每件物品可以选无数次,我们同样是用f[i]表示体积为i的背包所能获得的最大价值,状态转移方程也和01背包相同,只不过枚举的顺序不同
之前我们说01背包问题中背包体积是从后往前枚举,而完全背包问题不一样,它是从前往后枚举,这样的话,同一个物品就可能会被多次选取(可以结合01背包理解一下),就可以了
代码(代码就把第二层的j循环顺序改了一下):
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int M=205;
int f[M];
int main()
{
int m,n,i,j,v,w;
scanf("%d%d",&m,&n);
memset(f,0,sizeof(f));
for(i=1;i<=n;++i)
{
scanf("%d%d",&v,&w);
for(j=v;j<=m;++j)
f[j]=max(f[j],f[j-v]+w);
}
printf("%d",f[m]);
return 0;
}
【多重背包问题】
问题:有n个物品和一个容量为m的背包,给出每件物品的体积v[i]和价值w[i],每件物品可以选s[i]次,求能获得的最大价值
分析:多重背包问题又换了一种花样,物品不是选一次,也不是无数次,而是给定的数s[i]次,那该怎么做呢
我们考虑,若每件物品有s[i]个,那我们可以把它看做是s[i]个不同的物品,只不过每件物品的体积都为v[i],价值都为w[i],每件物品只能选1次,这样的话,这个多重背包问题就可以转换成01背包问题
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int M=6005;
int f[M];
int main()
{
int m,n,i,j,k,v,w,s;
scanf("%d%d",&n,&m);
memset(f,0,sizeof(f));
for(i=1;i<=n;++i)
{
scanf("%d%d%d",&v,&w,&s);
for(k=1;k<=s;++k)
for(j=m;j>=v;--j)
f[j]=max(f[j],f[j-v]+w);
}
printf("%d",f[m]);
return 0;
}
【混合三种背包问题】
问题:这一类题就是将01背包,完全背包,多重背包混合起来,也就是有的物品可以选一次,有的物品可以选无数次,有的物品可以选取的个数有一个上限
其实就是三类背包,在做的时候判断一下就可以了
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=105;
int f[N];
int main()
{
int n,m,i,j,k,v,w,s;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
{
scanf("%d%d%d",&v,&w,&s);
if(s==0) //0表示的是完全背包,其他的是01背包或者是多重背包
{
for(j=v;j<=m;++j)
f[j]=max(f[j],f[j-v]+w);
}
else
{
for(k=1;k<=s;++k)
for(j=m;j>=v;--j)
f[j]=max(f[j],f[j-v]+w);
}
}
printf("%d",f[m]);
return 0;
}
【二维费用的背包问题】
问题:这一类的问题是指,对于一件物品,具有两种不同的费用,选择这件物品的话必须同时付出这两种代价,其他的条件不变,然后还是求能获得的最大价值
有两种费用了怎么办呢,加一维就可以了
首先物品的两种费用我们用u和v表示,定义f[i][j]表示第一种费用为i,第二种费用为j时能获得的最大价值
动态转移方程:f[i][j]=max{f[i][j],f[i-u][j-v]+w}
理解:还是一样的想法,要想放入这件物品,必须要给先它腾一个位置再放
代码(为了方便找的是01背包的问题):
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=505;
int f[N][N];
int main()
{
int n,x,y,i,j,k,u,v,w;
scanf("%d%d",&x,&y);
scanf("%d",&n);
for(i=1;i<=n;++i)
{
scanf("%d%d%d",&u,&v,&w);
for(j=x;j>=u;--j)
for(k=y;k>=v;--k)
f[j][k]=max(f[j][k],f[j-u][k-v]+w);
}
printf("%d",f[x][y]);
return 0;
}
【分组的背包问题】
问题:有n件物品和一个容量为m的背包,每件物品的体积为v价值为w,将这些物品划分成若干个组,每个组的物品互相冲突(说人话就是每个组最多只能选一件物品),依然是求能获得的最大价值
那么我们考虑,对于每组物品,你可以选一件,也可以不选(是不是有点像01背包)
正解是这么做的:首先枚举一遍所有的组,再枚举背包容量,然后枚举这一组内的每一个物品,最后转移一下就可以了
理解:后面的两个枚举只会选取最优的那一个物品(或者不选),那么在前面枚举所有的组,就能实现在所有不同的组中选取一个物品(或者不选)使结果最优了
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1005;
int v[N],w[N],f[N],num[N],rec[N][N];
int main()
{
int n,m,i,j,k,x,s,t=0;
scanf("%d%d",&m,&n);
for(i=1;i<=n;++i)
{
scanf("%d%d%d",&v[i],&w[i],&s);
if(!num[s])
t++;
num[s]++;
rec[s][num[s]]=i;
}
for(k=1;k<=t;++k)
for(j=m;j>=0;--j)
for(i=1;i<=num[k];++i)
{
if(j<v[rec[k][i]])
continue;
x=rec[k][i];
f[j]=max(f[j],f[j-v[x]]+w[x]);
}
printf("%d",f[m]);
return 0;
}
【有依赖的背包问题】
【背包问题的方案总数】
【初始化的细节】
如果背包不需要被装满(大多数情况都是这样),那么f数组全部赋为0
如果背包必须要被装满,那么除了f[0]=0外其他的值全部赋为-maxlongint(即很小的数)
至于解释可以戳这里背包问题初始化的细节