【ACM】0-1背包问题

特点:每个物品仅有一个。
0-1背包问题中的0.1一般表示物品取或者不取的2种状态。这是其他背包问题的基础。

如果采取贪心算法,求出的结果是错误的,如计算每一个物品的Vi/Wi,然后把物品进行从大到小进行排序。

0-1背包问题的状态转换方程 :f[i][j] = max(f[i-1][j-Wi] + V[i] , f[i-1][j])
f[i][j]表示在前i件中物品中选择若干件(不是所有物品,只是最优的选择一部分)放在承重为j的背包中可以取得最大价值。

状态转移方程表示的是对第i件物品取或者不取的决策。Wi表示第i件物品的质量,Vi表示第i件物品的价值,第一项表示第i件物品被取,第二项表示第i件物品不取,则会转移到i-1个物品中的状态。

理解DP的关键在于能否正确的打表,并找出规律(熟悉以后就可以放弃表格的方式)。

代码如下:

#include <cstdio>
#include <iostream>
#include <cstring>

#define maxn 1000

using namespace std;

int max(int x,int y)
{
    return x>y?x:y;
}

int main()
{
    int weight[maxn];
    int value[maxn];
    int f[maxn][maxn];
    memset(weight,0,maxn);
    memset(value,0,maxn);
    int n; // 物品个数
    int m; // 背包重量
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&weight[i],&value[i]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if(weight[i]<=j)
                f[i][j] = max(f[i-1][j-weight[i]]+value[i] , f[i-1][j]);
            else 
                f[i][j] = f[i-1][j];

        }
    printf("%d\n",f[n][m]);
    return 0;
}

上述算法可以进一步优化内存,在计算f[i][j]的时候只使用了f[i-1][0…j],而没有使用其他的子问题,因此在存储子问题的解时候,只需要存储f[i-1]的解即可(我们需要的是最终的f[n][m],因此可以一步步的舍弃前面的子问题的解,只需要保存上一步的结果即可)。因此可以使用2个一维数组进行实现,一个人数组存储上一步子问题的解,一个数组存储正在解决的子问题。
内存优化:
状态转移方程f[i][j] = max(f[i-1][j-Wi] + V[i], f[i-1][j])中因为当前状态仅仅依赖于前一状态的剩余体积和Vi的值,所以状态转移方程可以简化为一维数组f[j]=max(f[j],f[j-w[i]]+v[i]) 。方程的两侧都有f[j],等号左边的是当前状态,右边的是前一状态,为了保证状态转移的正确性(也就是说是转移到前一状态还是对第i个物品获取的状态),应该首先更新等号左边的f[j]。因此内层循环应该从大到小进行循环。

#include <cstdio>
#include <iostream>
#include <algorithm>
#define maxn 1000
using namespace std;
int max(int x,int y)
{
    return x>y?x:y;
}
int main()
{
    int n;
    int m;
    int weight[maxn];
    int value[maxn];
    int f[maxn]={0};
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d %d",&weight[i],&value[i]);
    for(int i=1;i<=n;i++)
        for(int j=m;j>=weight[i];j++)
          f[j]=max(f[j],f[j-weight[i]]+value[i]);
    printf("%d\n",f[m]);
    return 0;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值