【dp/01背包翻转】NYOJ 又见01背包

又见01背包

时间限制:1000 ms | 内存限制:65535 KB

难度:3

描述

有n个重量和价值分别为wi 和 vi 的 物品,从这些物品中选择总重量不超过 W 的物品,求所有挑选方案中物品价值总和的最大值。
  1 <= n <=100
  1 <= wi <= 10^7
  1 <= vi <= 100
  1 <= W <= 10^9

输入

多组测试数据。
每组测试数据第一行输入,n 和 W ,接下来有n行,每行输入两个数,代表第i个物品的wi 和 vi。

输出

满足题意的最大价值,每组测试数据占一行。

样例输入

4 5
2 3
1 2
3 4
2 2

样例输出

7

来源

飘谊系列

上传者

TC_张友谊


题意

01背包


思路

传统的01背包是每次检查在负重为j的情况下当前背包可以装载的最大价值,在本题当中无疑背包负重极大,这样操作不可行
不过背包的最大价值并不大,我们可以考虑另外一种情况下的装载方式
我们让背包dp里面的数值为在价值为j情况下的最小重量,这样一来我们可以根据传统的01背包的模型构建出一个新的状态转移方程

dp[j] = min(dp[j],dp[j - val[i]] + wei[i]);

自然,在相同价值的情况下,背包的重量越少越好,所以我们这样子是可行的。
那么,我们最后只要使用所有物品的最大价值向下遍历(因为我们就是拿价值当dp数组的索引)直到找到一个dp当中的重量满足背包的最大容量,那么此时的价值就是最大价值。


坑点

无。


AC代码

#include
using namespace std;
#define maxn 50000
#define inf 0x3f3f3f3f
int dp[maxn];
int wei[maxn],val[maxn];

void solve(void)
{
int n,m;

    while(~scanf("%d%d",&n,&m))
    {
        int valsum = 0;
        for(int i = 0 ; i < n ; i++)
        {
            scanf("%d%d",&wei[i],&val[i]);
            valsum += val[i];
        }
        memset(dp,inf,sizeof dp);
        dp[0] = 0;
        for(int i = 0 ; i < n ; i++)
        {
            for(int j = valsum ; j >= val[i] ; j--)
            {
                dp[j] = min(dp[j],dp[j - val[i]] + wei[i]);
            }
        }
        for(int i = valsum ; i >= 0 ; i--)
        {
            if(dp[i] <= m)
            {
                printf("%d\n",i);
                break;
            }
        }
    }
}

int main(void)
{
    solve();
    return 0;
}

你可能感兴趣的:(DP)