背包问题系列--"0-1背包问题"

背包问题一直以来就是一类十分重要的题。而其中以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的思路与处理方式。
欲知后续背包,请看下集。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值