HDU 4778 2013 ACM/ICPC 杭州赛区现场赛 I. Gems Fight!

这题想了非常之久,AC之后必须写结题报告啊!!!!!!!

其实最终还是不能算自己想出来的,还是靠问了网上写解题报告的大牛,才算明白了。确实觉得自己思维有缺陷,只会往一个方向去想,没想出来不知道稍微变化一下。这题显然是状态压缩DP,因为包21个,所以可以用一个二进制表示包有没有被取,其中 1 表示还没被取,dp[i] 表示 初始可取包状态为 i 的时候 先手利用剩下的包还能取到的最大值。那么我们要求的就是 dp[(1 << b) - 1],如何转移呢? 对于某个局面 i,假设我们取了第 x 个包,(应该要有 (i & (1 << x) == 1))那么取了这个包后能不能得到魔法石我们是可以知道的,如果能得到一些魔法石,设为 y,那么 如果 y > 0, 取完下一轮还是先手取,所以 dp[i] = max(dp[i], y + dp[i ^ (1 << x)]), 如果y == 0,那么下一轮换人取了,那么对于这轮的人来说就是取了对手剩下的,而对手的最优值为dp[i ^ (1 << x)], 所以 dp[i] = max(dp[i], left - dp[i ^ (1 << x)]). 其中 left 可以预处理出来。

整个过程用深搜来完成,需要记忆化。

#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cctype>
#include <bitset>
#include <ctime>
#include <set>
#include <list>
#include <stack>
#include <queue>
#include <deque>
#include <string>
#include <vector>
#include <map>
#pragma warning (disable : 4996)
#define mem(a) memset(a, 0, sizeof(a))
#define dou double
#define LL long long
#define N (1 << 22)
#define Mod 1000000007
#define sl(a) strlen(a)
#define eps 1e-8
#define inf 2000000000
using namespace std;

int dp[N], num[N], bag[25][10], fl[N], vis[N], g, b, s, lim;

void Init(){
        int c[10];
    for (int i = 1; i < (1 << b); ++i){
                mem(c);
        for (int k = 0; k < b; ++k){
            if (i & (1 << k)){
                for (int j = 1; j <= g; ++j) c[j] += bag[k][j];
            }
        }
        for (int j = 1; j <= g; ++j) num[i] += c[j] / s;
    }
}

int OK(int t, int i){
    return num[t + (1 << i)] - num[t];
}

int dfs(int t, int s){
    int i, j, k, tem;
    if (vis[t]) return dp[t];
    vis[t] = 1;
    for (i = 0; i < b; ++i){
        if (t & (1 << i)){
            if (tem = OK(t ^ lim, i)){
                dp[t] = max(dp[t], tem + dfs(t ^ (1 << i), s - tem));
            }
            else dp[t] = max(dp[t], s - dfs(t ^ (1 << i), s));
        }
    }
    return dp[t];
}

int main(){
    int n, t, ca = 1, i, j, k, cnt, re, sum, tem, x, id;
    //freopen("in,txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    //ios :: sync_with_stdio(false);
    while (scanf("%d%d%d", &g, &b, &s), g | b | s){
        for (i = 0; i < b; ++i){
            scanf("%d", &n);
            for (j = 1; j <= n; ++j) {
                scanf("%d", &tem);
                bag[i][tem]++;
            }
        }
        Init();
        sum = num[(1 << b) - 1]; lim = (1 << b) - 1;
        re = dfs((1 << b) - 1, sum);
        //for (i = 0; i < (1 << b); ++i) cout << i << ' ' << dp[i] << endl;
                printf("%d\n", 2 * re  - sum);
                   mem(bag), mem(num), mem(fl), mem(dp), mem(vis);
    }

    return 0;
}
这种和为定值的博弈,刘汝佳白书上还有一题,那题倒是很快做出来了,这题我的思路和上面的正解是一个反的过程,我原来想直接用dp[i]表示状态为 i (1表示已经取了,0表示没取)先手能去得到的最大值,但这样完全不能判断清楚状态 i 时是谁在取,因为过程中会换人。。。。直接GG!!!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值