ZOJ 3164 Cookie Choice(终极背包)

题意:

给出N类物品,有价值,数量,价格

还有一些限制组,限制的组里面最多只能选一类物品,问D钱最多能获得的价值,并且D钱必须花完

思路:

看完背包9讲,感觉这一题还是不难理解的。首先,对于没有组的限制的X类物品,进行一遍背包(01背包、完全背包、多重背包)。

其次,对于有组的限制的物品,就需要再次考虑下了。这次要用到泛化背包和分组背包的思想。每一组只能选择组里面的一类物品。

于是试图考虑,如果我分给这个组V(range from 0 to D)的容量,能够最多拿到多少价值。因为这个组有X类物品,只能选择一类,

所以需要先对这个组的每一类物品进行一次01背包,获得分配给这类物品V的容量取得的最大价值,然后再对这个组的X类物品进行分组背包。

最后,针对上述组进行一次分组背包,问题得以解决。

(第一次开始自己尝试描述算法思路,Orz Orz,坚持坚持)

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>

using namespace std;

#define max(a,b) (((a) > (b)) ? (a) : (b))

const int MAXN = 1500;
const int INF = 1e9;
int Ki[MAXN], Ei[MAXN], Pi[MAXN];
int w[10][MAXN], f[MAXN];
int n, d, g;

void init_input()
{
    for (int i = 1; i <= n; ++i)
        scanf("%d %d %d", &Ki[i], &Ei[i], &Pi[i]);

    scanf("%d%*c", &g);
    memset(f, 0, sizeof(f));
    char s[1000];
    for (int i = 1; i <= g; ++i)
    {
        gets(s);
        int j = 0;
        while (j < strlen(s))
        {
            if ('1' <= s[j] && s[j] <= '9')
            {
                int sum = 0;
                while ('0' <= s[j] && s[j] <= '9')
                    sum = 10 * sum + s[j] - '0', ++j;
                f[sum] = i;
            } 
            else 
                ++j;
        }
    }
}

void init_dp(int dp[])
{
    fill(dp, dp + d + 1, -INF);
    dp[0] = 0;
}

void complete_pack(int w, int v, int dp[])
{
    for (int i = w; i <= d; ++i)
        if (dp[i-w] > -INF)             // 必须装满的时候,是需要这么一个判断的。-INF表示没有合法解
            dp[i] = max(dp[i], dp[i-w] + v);
}

void zero_one_pack(int w, int v, int dp[])
{
    for (int i = d; i >= w; --i)
        if (dp[i-w] > -INF)
            dp[i] = max(dp[i], dp[i-w] + v);
}

void multiple_pack(int w, int v, int n, int dp[])
{
    int i = 1, c = n;

    while (i < c)
    {
        zero_one_pack(i * w, i * v, dp);
        c -= i;
        i *= 2;
    }
    if (c)
        zero_one_pack(c * w, c * v, dp);
}

int main()
{
    while (scanf("%d %d", &n, &d) != EOF)
    {
        init_input();

        int dp[MAXN], temp[MAXN];

        init_dp(dp);
        for (int i = 1; i <= g; ++i)
            init_dp(w[i]);

        for (int i = 1; i <= n; ++i)
        {
            if (f[i])
                init_dp(temp);

            if (!Ki[i] || Ki[i] * Pi[i] >= d)
                complete_pack(Pi[i], Ei[i], f[i] ? temp : dp);
            else
                multiple_pack(Pi[i], Ei[i], Ki[i], f[i] ? temp : dp);

            if (f[i])   
                for (int j = 0; j <= d; ++j)
                    w[f[i]][j] = max(w[f[i]][j], temp[j]);
        }
        for (int i = 1; i <= g; ++i)    // w[g][d] : g个组,每个组有d个物品,每个组最多选择其中一个
            for (int j = d; j >= 0; --j)
                for (int k = 0; k <= j; ++k)
                    if (w[i][k] > -INF && dp[j-k] > -INF)
                        dp[j] = max(dp[j], dp[j-k] + w[i][k]);

        if (dp[d] >= 0)
            printf("%d\n", dp[d]);
        else 
            printf("i'm sorry...\n");
    }
    return 0;
}

 

 

posted @ 2012-11-08 17:18  kedebug  阅读(404)  评论(0编辑  收藏  举报