浅谈背包
01背包:
问题:有N件物品和一个容量为V的背包。第i件物品的体积是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
思路:对每种物品我们可以选择用或不用,则有状态转移方程\(f[i][j]=max(f[i-1][j-w[i]]+v[i],f[i][j])\)
Code:
for(int i=1;i<=n;i++)
for(int j=1;j<=V;j++)
if(j>=w[i]) f[i][j]=max(f[i-1][j-w[i]]+v[i],f[i][j])
事实上我们发现这个方程内当前状态的转移之和上一个物品有关,则我们可以考虑优化空间
我们倒序dp,数组变成一维,\(f[j]=max(f[j-w[i]]+v[i],f[j])\),倒序dp的原因是为了不覆盖上次的值
Code:
for(int i=1;i<=n;i++)
for(int j=V;j>=1;j--)
if(j>=w[i]) f[j]=max(f[j-w[i]]+v[i],f[j]);
完全背包:
问题:有N件物品和一个容量为V的背包。第i件物品的体积是w[i],价值是v[i],每件物品可以用无限次。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
思路:状态转移方程不变,但由于每件物品可以用无数次,则不用考虑上件物品的影响,正序dp即可
Code:
for(int i=1;i<=n;i++)
for(int j=1;j<=V;j++)
if(j>=w[i]) f[j]=max(f[j-w[i]]+v[i],f[j]);
多重背包:
问题:有N件物品和一个容量为V的背包。第i件物品的体积是w[i],价值是v[i],每件物品可以用u次。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
思路:可以看做是可取u个的01背包来做
Code:
for(int i=1;i<=n;i++)
for(int j=V;j>=1;j--)
for(int k=1;k<=u;k++)
if(j>=k*w[i]) f[j]=max(f[j-k*w[i]]+k*v[i],f[j]);
事实上我们可以进行优化,可以将u进行二进制分解,然后再来dp,时间复杂度O(VN log u)
Code:
int b=1;
while(u){
if(u&1) a[++cnt]=b;
b<<=1;u>>=1;
}int t=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=cnt;j++)
w0[++t]=w[i]*a[j],v0[t]=v[i]*a[j];
for(int i=1;i<=t;i++)
for(int j=V;j>=1;j--)
if(j>=w0[i]) f[j]=max(f[j-w0[i]]+v0[i],f[j]);
然而还有O(VN)的单调队列优化,然而...我还不会...