01背包问题
完全背包问题
基本题型:背包的体积为m,有n类物品它们个数不限,每类物品有不同的价值v和不同的体积w,计算背包最多能装多少价值的物品。
和01的区别:01的个数限制,每类只有一件,要么取要么不取,完全的个数不限,每类可以取1件或多件或不取。
普通方法:
memset(d,0,sizeof(d));
for(i=1; i<=n; i++)
{
for(j=0; j<=m; j++)
{
for(k=0;k*w[i]<=j;k++)
{ if(j-w[i]>=0)
d[i][j]=max(d[i-1][j],d[i-1][j-k*w[i]]+k*v[i]);
else d[i][j]=d[i-1][j];
}
}
}
cout<<d[n][m]<<endl;
同01背包相似,多了一层循环。
优化:01背包时,因为每类物品1件,所以考虑当前位置取与不取,是在前一类的基础上计算的,反映到代码中,就是每一类用一个i,而完全背包,每类多件,我们不仅要考虑前一类的情况,还要考虑当前类取的情况(取与不取,取多少件),实际上是在本类取的件数上一件件增加考虑的,优化如下:
memset(d,0,sizeof(d));
for(i=1; i<=n; i++)
{
for(j=w[i]; j<=m; j++)
{
d[j]=max(d[j],d[j-w[i]]+v[i]);
}
}
cout<<d[m]<<endl;
与01的区别是内循环从小到大循环,这样就做到了本类从取少件到多件的考虑。
详细解释在01里讨论过了01背包,戳这里
多重背包问题
基本题型:有n种物品和一个容量为m的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大
和完全背包的区别:每类不是无限件,而是限制了件数,一般大于一件。
普通方法:
memset(d,0,sizeof(d));
for(i=1; i<=n; i++)
{
for(j=0; j<=m; j++)
{
for(k=0;k<=n[i]&&k*w[i]<=j;k++)
{ if(j-w[i]>=0)
d[i][j]=max(d[i-1][j],d[i-1][j-k*w[i]]+k*v[i]);
else d[i][j]=d[i-1][j];
}
}
}
cout<<d[n][m]<<endl;
优化:考虑用一维数组,转化成01背包问题,考虑到二进制思想,一个数一定可以由2的不同幂之和表示。比如:3=1+2;5=1+4;6=2+4;7=1+2+4……,我们按照这种思想把第i种物品分成若干件不同的物品,其中每个不同的物品有一个系数,这些系数分别为1,2,4,……2^(k+1)
,且k是满足n[i]-2^k+1>0的最大整数,这件新物品的费用和价值均是原来的费用和价值乘以这个系数。例如,如果n[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品,(取6是为了不和前面的重复,保证新分成的每个都是不同的物品)。
二进制的不同优化方法:
for(i=0;i<n;i++)
{
for(j=1;j<=a[i].num;j<<=1){ //分成不同的件,<<=等同于*=2
v[t]=j*a[i].v;
w[t]=j*a[i].w;
t++;
a[i].num-=j;
}
if(a[i].num>0){ //最后一项补齐
v[t]=a[i].num*a[i].v;
w[t]=a[i].num*a[i].w;
t++;
}
}
或者
for (j = 1; j * a[i] <= m && j <= x; x -= j, j *= 2)
b[++t] = j * a[i];
if (x)
b[++t] = x * a[i];//补齐最后一项
一篇很好的博客:
poj1014完全背包 hdu2191多重背包 经典二进制优化