题意:给定一个有向图,每个结点有一个ki,然后每次游戏给每个结点一开始一个值,每次轮流选一个位置,满足它能到下一个结点,并且值为正,把值-1,然后在周围结点选k[i]个+1,问最后谁不能操作谁输,问每次游戏输赢
思路:先在图上构造sg函数,由于每个结点最多连接15个结点,这样就可以枚举加了奇数次1的是哪些结点,因为偶数是等于不影响,(先手取了后手跟着取一次就不影响),利用一个状态压缩,然后就可以根据状态转移求出sg函数,然后每轮游戏只要考虑奇数位置的Nim和即可
代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <map> using namespace std; const int MAXS = (1<<15); const int N = 105; int t, n, m, k[N], sg[N], cnt[MAXS]; vector<int> g[N]; int bitcount(int x) { int ans = 0; while (x) { ans += (x&1); x >>= 1; } return ans; } int dfs(int u) { if (sg[u] != -1) return sg[u]; if (g[u].size() == 0) return sg[u] = 0; for (int i = 0; i < g[u].size(); i++) dfs(g[u][i]); int maxs = (1<<g[u].size()); map<int, int> vis; for (int i = 0; i < maxs; i++) { if (cnt[i] > k[u]) continue; if ((cnt[i]^k[u])&1) continue; int x = 0; for (int j = 0; j < g[u].size(); j++) { if (i&(1<<j)) x ^= sg[g[u][j]]; } vis[x] = 1; } for (int i = 0; ; i++) if (!vis.count(i)) return sg[u] = i; } void getsg() { memset(sg, -1, sizeof(sg)); for (int i = 0; i < n; i++) dfs(i); } int main() { for (int i = 0; i < MAXS; i++) cnt[i] = bitcount(i); int cas = 0; scanf("%d", &t); while (t--) { scanf("%d%d", &n, &m); for (int i = 0; i < n; i++) g[i].clear(); int u, v; while (m--) { scanf("%d%d", &u, &v); g[u].push_back(v); } for (int i = 0; i < n; i++) scanf("%d", &k[i]); getsg(); scanf("%d", &m); printf("Game#%d:\n", ++cas); int rou = 0; while (m--) { int sum = 0, x; for (int i = 0; i < n; i++) { scanf("%d", &x); if (x&1) sum ^= sg[i]; } printf("Round#%d: %s\n", ++rou, sum == 0 ? "LOSING" : "WINNING"); } printf("\n"); } return 0; }
UVA 12163 - Addition-Subtraction Game(博弈),布布扣,bubuko.com
UVA 12163 - Addition-Subtraction Game(博弈)
原文:http://blog.csdn.net/accelerator_/article/details/38401015