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

发布时间:2014-10-23 23:30:26
来源:分享查询网

这题想了非常之久,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!!!!!!

返回顶部
查看电脑版