背包问题是属于动态规划中的一种非常经典的运用,小编在这里大力推荐dd_engi的背包九讲,这种超级详细的讲解在小编心中是可以得到诺贝尔文学奖的~
1.01背包
首先是最基础的01背包问题,所有元素只能使用一次,此问题只需要考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物 品放入容量为v的背包中”,价值为f[i-1][v];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”,此时能获得的最大价值就是f[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i]。
所以我们能够得到01背包的状态转移方程:f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}。
#include //一维数组
#include
#include
using namespace std;
int v[1001],p[1001];
int dp[1002];
int main()
{
int t,n,V,i,j;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&V);
for(i=0;i=v[i];j--)
{
dp[j]=max(dp[j],dp[j-v[i]]+p[i]);
}
printf("%d\n",dp[V]);
}
return 0;
}
小编在这里说明一下,01背包问题是可以用到二维数组的,但一维数组在优化上代码的简洁上,都是要比二维的要好,所以一维的使用更加广泛。
2.完全背包
完全背包非常像01背包,只是在元素的使用上变成了无限次使用。小编先给出完全背包的伪代码。
for i=1..N
for v=0..V
f[v]=max{f[v],f[v-cost]+weight}
先想想为什么在01背包中要按照v=V..0的逆序来循环。这是因为要保证第i次循环中的状态f[i][v]是由状态f[i-1] [v-c[i]]递推而来。换句话说,这正是为了保证每件物品只选一次,保证在考虑“选入第i件物品”这件策略时,依据的是一个绝无已经选入第i件物品的 子结果f[i-1][v-c[i]]。而现在完全背包的特点恰是每种物品可选无限件,所以在考虑“加选一件第i种物品”这种策略时,却正需要一个可能已选入第i种物品的子结果f[i][v-c[i]],所以就可以并且必须采用v=0..V的顺序循环。这就是这个简单的程序为何成立的道理。
这里小编顺便来介绍一下背包的满存储问题,这是一个很简单的道理,我们只需要把dp的初始dp[0]=0,其他元素都为-INF即可,这样最后有值而非-INF的就是达到了满存储的状态,只有一路从0这个位置开始的才是合法的。
#include //完全背包(满存储)
#include
#include
#include
#define maxn 10000+10
#define INF 0x3f3f3f3f
using namespace std;
int val[maxn],cost[maxn];
int dp[maxn];
int main()
{
int t,e,f,n;
cin>>t;
while(t--)
{
cin>>e>>f;
int V=f-e;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>val[i]>>cost[i];
}
memset(dp,-INF,sizeof dp);
dp[0]=0;
for(int i=1;i<=n;i++)
{
for(int j=cost[i];j<=V;j++)
{
dp[j]=max(dp[j],dp[j-cost[i]]+val[i]);
}
}
if(dp[V]>0)printf("%d\n",dp[V]);
else printf("This is impossible.\n");
}
return 0;
}
3.多重背包
多重背包问题其实从本质来讲就是对01背包和完全背包的灵活运用,对于那些数量*价值>总容量的元素,我们可以认为它对于背包来说是无限个的,因为怎么取都不可能把所有元素取完,那数量*价值<总容量的元素我们也可以经过灵活处理把它变成01背包问题,这里需要用到线性代数的极大线性无关组的思想,我们把数量拆成1.2.4.8……这样的,因为从二进制的角度讲由于1.2.4.8……这样的组合每一个数的二进制码都不会在同一位置重复,所以很明显这组数是可以组合出任何一组数的,所以我们就不用对这种元素一个一个的进行01背包处理,很好的提高了效率。
#include //多重背包
#include
using namespace std;
int dp[105],v,n[105],vl[105],ct[105];
void ZeroOnePack(int cost,int value)
{
for(int i=v; i>=cost; i--)
dp[i]=max(dp[i],dp[i-cost]+value);
}
void CompletePack(int cost,int value)
{
for(int i=cost; i<=v; i++)
dp[i]=max(dp[i],dp[i-cost]+value);
}
void MultiPack(int cost,int value,int num)
{
int k;
if(num*cost>=v)
CompletePack(cost,value);
else
{
k=1;
while(num>k)
{
ZeroOnePack(k*cost,k*value);
num-=k;
k*=2;
}
ZeroOnePack(num*cost,num*value);
}
}
int main()
{
int c,m,i;
scanf("%d",&c);
while( c--)
{
scanf("%d%d",&v,&m);
for( i=1; i<=m; i++)
scanf("%d%d%d",&vl[i],&ct[i],&n[i]);
memset(dp,0,sizeof(dp));
for( i=1; i<=m; i++)
MultiPack(vl[i],ct[i],n[i]);
printf("%d\n",dp[v]);
}
}