首页 > 代码库 > poj 2184 0---1背包的变形

poj 2184 0---1背包的变形

这题是0--1背包的变形,对理解0--1背包有很大的帮组


题意:要选一些牛去参见展览,每个牛有幽默、智慧两个选择标准,要求选的这些牛使得幽默和智慧的总和最大且幽默和智慧的每个总和都必须是大于等于0;


刚看的这个题目是时候,知道是一个0--1背包的的题目,但就是不知道怎么来写出状态转移方程,因为题中的两个变量都是有负值的。

看了大牛的解题报告才知道。

我们可以把幽默个变量看成是体积 , 智慧看成是价值。

我们可以把每个牛幽默的值 , 放在一个坐标上,让后整体往右移,使得最小值为 0 , 那么这时候就不存在负值了。

由题意我们可以知道,幽默的最小值为:100*(-1000) , 也就是 -100000 

那么我们就使每个幽默都 + 100000 , 那么这个体积(幽默)就不存在负值了。

但是当我们进行状态转移时 , 要分两种情况 :

1、体积(幽默) >= 0 

这时就更普通的0---1背包一样 , 从大到小转移

dp[j] = max(dp[j] , dp[j-fi[i]]+ti[i]);


2、体积(幽默)< 0

这时在状态转移时 和 普通的 0---1背包正好相反 , 因为一个数减去一个负数 , 那么就会变成一个比原来那个数大的数 , 这时我们就要从小的到大进行状态转移。

dp[j] = max(dp[j] , dp[j-fi[i]]+ti[i]);

通过这个题,更进一步的理解了0---1背包


当体积存在负数时:

我们首先要把体积转变为非负数

然后在进行状态转移时 , 体积为负数的情况时 , 要反过来进行状态转移


代码:

//体积存在负数时
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;

#define maxn 110
#define INF 200010
int fi[maxn] , ti[maxn];
int dp[INF];
int n;

int main()
{
    while(scanf("%d" , &n) != EOF)
    {
        int i , j;
        for(i = 1; i <= n; i++)
            scanf("%d %d" , &fi[i] , &ti[i]);
        for(i = 0; i <= 200000; i++)
            dp[i] = -INF*100;
        dp[100000] = 0;//这个位置表示0
        for(i = 1; i <= n; i++)
        {
            if(fi[i] >= 0)
            {
                for(j = 200000; j >= fi[i]; j--)
                    dp[j] = max(dp[j] , dp[j-fi[i]]+ti[i]);
            }
            else
            {
                for(j = fi[i]; j <= 200000+fi[i]; j++)
                    dp[j] = max(dp[j] , dp[j-fi[i]]+ti[i]);
            }
        }

        int ans = -INF*100;
        for(i = 100000; i <= 200000; i++)
            if(dp[i] >= 0)
                ans = max(ans , dp[i]+i-100000);
        cout<<ans<<endl;

    }
    return 0;
}