背包问题一直以来就是一类十分重要的题。而其中以DP为著称的“0-1背包”就是一道非常经典的题。
题目如下:
【基础算法】0-1背包问题 时间限制: 1 Sec 内存限制:64MB
有 n 件物品,
每件物品有一个价值和一个重量,分别记为: b1,b2, …bn w1,w2, …wn 其中所有的 重量wi 均为整数。
现有一个背包,其最大载重量为W,要求从这n件物品中任取若干件(这些物品要么被装入要么被留下)。问背包中装入哪些物品可使得所装物品的价值和最大?输入
第1行:2个整数n(1<=n<=1000)和W(1<=W<=10000),分别表示物品的件数和背包的最大载重量。
输出
第1行:1个整数,表示背包所能装下的物品的最大总价值。
第2-?行:每行3个用空格分开的整数,i, wi, bi,分别表示最优解中的物品的编号、重量和价值。
样例输入
4 5 2 3 3 4 4 5 5 6
样例输出
7 1 2 3 2 3 4
分析:由题意知,我们先定义n(物品件数),k(背包载重量),t[1005(i)](每件物品重量),m[1005(i)](每件物品价值)与f[1005(i)][10005(j)](前i个物品用载重量为j的背包的最大价值),l(为输出定义下标),pre[1005(i)](存答案)。
int n,k,t[1005],m[1005],f[1005][10005],pre[1005],l;
输入
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d%d",&t[i],&m[i]);
不难看出其状态转移方程式为递到上一个并判断
f[i][j]=max(f[i-1][j],f[i-1][j-t[i]]+m[i]);
但此处有一个值得注意的点:就是当前行可能没被上一行读入。
对于此情况,我们可以用判断语句或强势复制解决:
判断:
for(int i=1;i<=n;i++)
for(int j=1;j<=k;j++)
{
if(j>=w[i])
f[i][j]=max(f[i-1][j],f[i-1][j-t[i]]+m[i]);
else
f[i][j]=f[i-1][j];
}
复制:
for(int i=1;i<=n;i++)
{
memcpy(f[i],f[i-1],sizeof(f[i]));
for(int j=t[i];j<=k;j++)
f[i][j]=max(f[i-1][j],f[i-1][j-t[i]]+m[i]);
}
输出:
printf("%d\n",f[n][k]);
接下来就是找序号了,在这里我们可以运用for循环从末到前地查找坐标并用if判断。边判断边用数组存。
for(int i=n;i>=1;i--)
if(f[i][k]==f[i-1][k-t[i]]+m[i]&&t[i]<=k)
{
pre[++l]=i;
k-=t[i];
}
输出
for(int i=l;i>=1;i--)
printf("%d %d %d\n",pre[i],t[pre[i]],m[pre[i]]);
最后完整代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,k,t[1005],m[1005],f[1005][10005],pre[1005],l;
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d%d",&t[i],&m[i]);
for(int i=1;i<=n;i++)
{
memcpy(f[i],f[i-1],sizeof(f[i]));
for(int j=t[i];j<=k;j++)
f[i][j]=max(f[i-1][j],f[i-1][j-t[i]]+m[i]);
}
printf("%d\n",f[n][k]);
for(int i=n;i>=1;i--)
if(f[i][k]==f[i-1][k-t[i]]+m[i]&&t[i]<=k)
{
pre[++l]=i;
k-=t[i];
}
for(int i=l;i>=1;i--)
printf("%d %d %d\n",pre[i],t[pre[i]],m[pre[i]]);
}
通过本题的讲解,我们可以知道DP的思路与处理方式。
欲知后续背包,请看下集。