码迷,mamicode.com
首页 > 其他好文 > 详细

hdu 3236 Gift Hunting 01背包中有两个背包有两种物品+1次的免费券

时间:2015-02-20 18:32:50      阅读:205      评论:0      收藏:0      [点我收藏+]

标签:

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3236

 

这道题目属于那种

一看:吓尿了

再看:诶 可以做

一做:跪的不行不行的...

 

两个背包

相当于二维花费 但是每次只需要花1种

 

这次我的dp数组从一开始就想对了

dp[j][k][i]表示第一个背包装了j且第二个背包装了k且用了(1-i)张免费券后能得到的最大的开心值

 

我参考了dango的做法

先算把特殊物品取了 在这个过程中得到三维dp数组的所有合法状态【如果特殊物品没取完 那么结果是dp数组的所有位置均为-1】

然后再根据合法状态去取正常的物品

 

算两个背包【都是花费】和枚举免费的物品这三个过程一定不能分开

否则不能保证每件物品只取一次

 

算两种物品【不同的物品】一定要分开

【取那些必须取的东西不是背包问题】

【背包问题的机制在于一个【不断更新】 然而必取物品是不允许被“更新掉”的】

 

几个启发

假设费用是1维的

背包九讲中的思路是:

dp[i] = max(dp[i], dp[i - cost] + weight)

其实换个角度 我们也可以改为:

dp[i+cost] = max(dp[i + cost], dp[i] + weight)

这两种写法是等效的 做题的时候要根据题意灵活变通 不要把自己写绕了

【若转移方程中原状态有约束 那么采用第二种写法会比较直观】

 

dp数组是有更新顺序的

由于采用的写法省去了最高维(表示“前i个物品中”)

所以特别要注意改制的问题

复杂的递推式要在最后检查

【如果当前改变了dp[j][k][l]的值 必须确保下面过程中dp[j][k][l]不再作为判断条件或出现在等号右边】

以这道题为例

不管是算特殊物品还是普通物品

免费的哪一栏一定要放最后

哪怕一开始有点想不明白 最后检查的时候 应当看到

dp[j][k][1]的值在下面不仅作为判断条件 还出现在等号右边

换言之

【dp[j][k][1]已经更新为当前状态的值了 我们却在下面的计算中仍旧把它当做是上一个状态的值】

 

【转移方程中 原状态有约束的情况下 初始化一定要把除起始状态外的所有的状态都设为不合法】

 

 

 

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <stack>
#include <set>
#include <queue>
#include <vector>

using namespace std;

typedef long long ll;

const int maxn = 35005;
const int maxm = 10010;
const int INF = 100000;

int v1, v2, n;
int dp[510][60][2];
int p[310], h[310], s[310];

void special()
{
    int total = 0;

    for(int i = 0; i < n; i++)
    {
        if(s[i] != 1)
            continue;

        int pre_total = total;
        total += h[i];

        for(int j = v1; j >= 0; j--)
        {
            for(int k = v2; k >= 0; k--)
            {
                if(dp[j][k][1] == pre_total)
                {
                    if(j + p[i] <= v1)
                        dp[j + p[i]][k][1] = dp[j][k][1] + h[i];
                    if(k + p[i] <= v2)
                        dp[j][k + p[i]][1] = dp[j][k][1] + h[i];
                }

                if(dp[j][k][0] == pre_total)
                {
                    dp[j][k][1] = dp[j][k][0] + h[i];

                    if(j + p[i] <= v1)
                        dp[j + p[i]][k][0] = dp[j][k][0] + h[i];// = total;
                    if(k + p[i] <= v2)
                        dp[j][k + p[i]][0] = dp[j][k][0] + h[i];
                }


            }
        }
    }

    for(int j = v1; j >= 0; j--)
        for(int k = v2; k >= 0; k--)
            for(int i = 0; i <= 1; i++)
                if(dp[j][k][i] != total)
                    dp[j][k][i] = -1;

}

int normal()
{
    int ans = -1;

    for(int i = 0; i < n; i++)
    {
        if(s[i] != 0)
            continue;

        for(int j = v1; j >= 0; j--)
        {
            for(int k = v2; k >= 0; k--)
            {
                if(dp[j][k][0] != -1)
                {
                    if(j + p[i] <= v1 && dp[j][k][0] + h[i] > dp[j + p[i]][k][0])
                        dp[j + p[i]][k][0] = dp[j][k][0] + h[i];

                    if(k + p[i] <= v2 && dp[j][k][0] + h[i] > dp[j][k + p[i]][0])
                        dp[j][k + p[i]][0] = dp[j][k][0] + h[i];
                }

                if(dp[j][k][1] != -1)
                {
                    if(j + p[i] <= v1 && dp[j][k][1] + h[i] > dp[j + p[i]][k][1])
                        dp[j + p[i]][k][1] = dp[j][k][1] + h[i];

                    if(k + p[i] <= v2 && dp[j][k][1] + h[i] > dp[j][k + p[i]][1])
                        dp[j][k + p[i]][1] = dp[j][k][1] + h[i];
                }

                if(dp[j][k][0] != -1 && dp[j][k][0] + h[i] > dp[j][k][1])
                    dp[j][k][1] = dp[j][k][0] + h[i];

            }
        }
    }

    for(int j = v1; j >= 0; j--)
        for(int k = v2; k >= 0; k--)
            for(int i = 0; i <= 1; i++)
                ans = max(ans, dp[j][k][i]);

    return ans;
}

int main()
{
    //freopen("in.txt", "r", stdin);

    int kase = 0;
    while(scanf("%d%d%d", &v1, &v2, &n) == 3 && v1 != 0)
    {
        memset(dp, -1, sizeof(dp));
        dp[0][0][0] = 0;

        for(int i = 0; i < n; i++)
            scanf("%d%d%d", &p[i], &h[i], &s[i]);

        printf("Case %d: ", ++kase);

        special();
        int ans = normal();

        printf("%d\n\n", ans);
    }

    return 0;
}

 

hdu 3236 Gift Hunting 01背包中有两个背包有两种物品+1次的免费券

标签:

原文地址:http://www.cnblogs.com/dishu/p/4296606.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!