hdu3182Hamburger Magi(压缩状态dp)

题目描述:

Hamburger Magi

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1694    Accepted Submission(s): 625


Problem Description
In the mysterious forest, there is a group of Magi. Most of them like to eat human beings, so they are called “The Ogre Magi”, but there is an special one whose favorite food is hamburger, having been jeered by the others as “The Hamburger Magi”.
Let’s give The Hamburger Magi a nickname “HamMagi”, HamMagi don’t only love to eat but also to make hamburgers, he makes N hamburgers, and he gives these each hamburger a value as Vi, and each will cost him Ei energy, (He can use in total M energy each day). In addition, some hamburgers can’t be made directly, for example, HamMagi can make a “Big Mac” only if “New Orleams roasted burger combo” and “Mexican twister combo” are all already made. Of course, he will only make each kind of hamburger once within a single day. Now he wants to know the maximal total value he can get after the whole day’s hard work, but he is too tired so this is your task now!
 

 

Input
The first line consists of an integer C(C<=50), indicating the number of test cases.
The first line of each case consists of two integers N,E(1<=N<=15,0<=E<=100) , indicating there are N kinds of hamburgers can be made and the initial energy he has.
The second line of each case contains N integers V1,V2…VN, (Vi<=1000)indicating the value of each kind of hamburger.
The third line of each case contains N integers E1,E2…EN, (Ei<=100)indicating the energy each kind of hamburger cost.
Then N lines follow, each line starts with an integer Qi, then Qi integers follow, indicating the hamburgers that making ith hamburger needs.
 

 

Output
For each line, output an integer indicating the maximum total value HamMagi can get.
 

 

Sample Input
1 4 90 243 464 307 298 79 58 0 72 3 2 3 4 2 1 4 1 1 0
 

 

Sample Output
298
题目大意:给我们一些物品,有价值和大小,要求一定容量内放进物品价值最大,教练这个我熟,01背包~但是这道题的每个物品有一个限制条件,就是有些物品有一些前置物品,必须在那些物品都放进去之后它才能放进去,建图dfs?不太好,正解是压缩状态dp,比较好想的思路就是把所有状态找出来,然后一个一个判断是否其中所有点的前置点是否都在其中,不满足就不考虑这个状态,但是这样暴力会超时... 正解是初始化所有放置情况为负数,即都不满足要求,dp[0]=0,就0满足,然后遍历所有状态,在每种状态下对其中的物品进行选择更新,如果用于更新的状态<0,就直接跳过,时间大大减少...
暴力代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<string>
#include<math.h>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn = 20;
int val[20], cost[20], need[20], s[20], n, m;
int ans;
void init() {
    ans = 0;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++) {
        scanf("%d", &val[i]);
        if (i > 0)s[i] = s[i - 1] + val[i];
        else s[i] = val[i];
    }
    for (int i = 0; i < n; i++) {
        scanf("%d", &cost[i]);
    }
    for (int i = 0; i < n; i++) {
        int k; scanf("%d", &k);
        need[i] = 0;
        for (int j = 0; j < k; j++) {
            int t; scanf("%d", &t);
            need[i] |= (1 << (t - 1));//把每个物品的所有前置物品用二进制储存起来
        }
    }
}
int sum, cst;
void solve() {
    for (int s = 1; s <= (1 << n) - 1; s++) {
        sum = cst = 0;
        int f = 1;
        for (int i = 0; i < n; i++) {
            if (!(s & (1 << i)) ) {
                continue;
            }
            if ((s & need[i]) == need[i]) {
                sum += val[i];
                cst += cost[i];
            }
            else {
                f = 0; break;
            }
        }
        if (f&&cst<=m) {
            ans = max(ans, sum);
        }
    }
    printf("%d\n", ans);
}
int main() {
    //freopen("test.txt", "r", stdin);
    int t; scanf("%d", &t);
    while (t--) {
        init();
        solve();
    }
    return 0;
}

AC代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<string>
#include<math.h>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn = 20;
int val[20], cost[20], need[20], n, m;
int ans;
int dp[40000], cst[40000];
void init() {
    ans = 0;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++) {
        scanf("%d", &val[i]);
    }
    for (int i = 0; i < n; i++) {
        scanf("%d", &cost[i]);
    }
    for (int i = 0; i < n; i++) {
        int k; scanf("%d", &k);
        need[i] = 0;
        for (int j = 0; j < k; j++) {
            int t; scanf("%d", &t);
            need[i] |= (1 << (t - 1));
        }
    }
}
void sovle() {
    memset(dp, 0xf3, sizeof(dp));//都初始化为负数
    memset(cst, 0, sizeof(cst));
    dp[0] = 0;
    int al = (1 << n) - 1;
    for (int s = 1; s <= al; s++) {
        for (int j = 0; j < n; j++) {
            int p = (1 << j);
            if (!(s & p))continue;
            int pre_s = s ^ p;
            if (dp[pre_s] < 0)continue;//用于递推的pre_s不满足条件,跳过
            if ((pre_s & need[j]) == need[j]) {//如果用于递推的pre_s包含了我们所需的其他物品
                cst[s] = cst[pre_s] + cost[j];
                dp[s] = max(dp[s], dp[pre_s] + val[j]);
                if (cst[s] <= m) {
                    ans = max(ans, dp[s]);
                }
            }
        }
    }
    printf("%d\n", ans);
}
int main() {
    int t; scanf("%d", &t);
    while (t--) {
        init();
        sovle();
    }
    return 0;
}

 

posted @ 2021-03-16 09:41  cono奇犽哒  阅读(59)  评论(0)    收藏  举报