今天要谈论的是一堆奇形怪状、莫名其妙、精灵可爱的背包。
416.采药
最基础的01背包模板题,随随便便DP即可。
for (int i=1;i<=n;i++)
for (int j=m;j>=a[i];j--)
f[j]=max(f[j],f[j-a[i]]+b[i]);
417.集合求和
01背包求方案数。
for (int i=1;i<=m;i++)//从1-m枚举使用的数(m代表题目中的n)
for (int j=n;j>=i;j--)//从n的累加和到i枚举累加和(n代表1-m的累加和)
f[j]=f[j]+f[j-i];//f[j]的方案数为本身方案数+减去i的方案数
418.完全背包
最基础的完全背包模板题,随随便便DP即可。
for (int i=1;i<=n;i++)
for (int j=a[i];j<=m;j++)
f[j]=max(f[j],f[j-a[i]]+b[i]);
419.质数和分解
完全背包求方案数
这里要做一个质数的预处理
for (int i=1;i<=p;i++)//p为1-n质数的个数
for (int j=a[i];j<=n;j++)//a[i]为一个质数
f[j]=f[j]+f[j-a[i]];//f[j]的方案数为本身方案数+减去a[i]的方案数
420.逃亡的准备
这这这是一道多重背包题。我们可以使用神奇的二进制法来优化这个算法。
比如有一个数字叫做66。
则可以将其分解为1+2+4+8+16+32+3
通过这些神奇的数字就可以拼出1-n的所有数字。
核心代码
while (k<a1)
{
for (int j=m;j>=k*b[i];j--)
f[j]=max(f[j],f[j-k*b[i]]+k*c[i]);
a1-=k;
k*=2;
}
for (int j=m;j>=a1*b[i];j--)
{
f[j]=max(f[j],f[j-a1*b[i]]+a1*c[i]);
}
关于该题,如果数量*体积已经超过背包的容积,则可直接当成完全背包来做。
for (int j=b[i];j<=m;j++)
f[j]=max(f[j],f[j-b[i]]+c[i]);
就省了一堆莫名其妙的事了。
422.竞赛真理
每个物品有两种“体积”和两种“价值”,做两遍01背包即可。
for (int i=1;i<=n;i++)
for (int j=m;j>=min(b[i],b1[i]);j--)//min(b[i],b1[i])可以保证每次循环都有事干
{
if (j>=b[i])
f[j]=max(f[j],f[j-b[i]]+a[i]);//如果大于第一种体积,则可以选择这种
if (j>=b1[i])
f[j]=max(f[j],f[j-b1[i]]+a1[i]);//如果大于第二种体积,则可以选择这种
}
423.金明的预算方案
带有依赖性的背包,分为主件附件,须选择对应主件才可选择其附件。
题目中说明尽量选择价格*重要度值高的,也就是说
价格*重要度是该物品的价值。
for (int i=1;i<=n;i++)
if (c[i]==0)//是主件才进入循环
{
for (int j=1;j<=a[i];j++)
h[j]=0;//清空辅助数组h
for (int j=a[i];j<=m;j++)
h[j]=f[j-a[i]]+b[i];//给辅助数组赋值(h[j]表示剩下钱为j时最大价值)
for (int j=1;j<=n;j++)
if (c[j]==i)//如果找到了以i为主件的一件物品
{
for (int k=m;k>=a[i]+a[j];k--)
h[k]=max(h[k-a[j]]+b[j],h[k]);//做一遍01背包
}
for(int j=a[i];j<=m;j++)
if (h[j]>f[j])f[j]=h[j];//取大值
}