您的位置:首页 > 其它

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

2013-11-18 21:48 525 查看
这题想了非常之久,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
, num
, bag[25][10], fl
, vis
, 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!!!!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  游戏 杭州