http://codeforces.com/contest/787/problem/C
题目大意:有一个长度为n的环,第1个位置是黑洞,其他都是星球。已知在星球上(不含第一个黑洞)有一位神。有两个人,每个人有一个集合的数字,两人进行游戏,每人每轮可以让神从一个星球向后移动x位(x为目前两个人所拥有的集合中的一个任意数字,数字可以重复选)。请求出神在2~n的每一个位置上时,两人分别先手的输赢情况,先手胜利输出WIN,先手必败输出LOOS,会无限循环输出LOOP。
思路:经典的有向图博弈(可惜我不会TAT)。
假定范围是[0,n-1],那么定义dp(i, j)表示是第i个人,在第j个位置先手的情况(三种情况,loop,lose,win)。然后我们知道了在dp[0][0] = dp[1][0] = lose是必败的,所以我们反向回去推即可。然后反向推是利用bfs进行的。
①因为对于某个点,如果是必败态,那么他之间的状态都是必胜的
②如果当前点是必胜态,那么他之前的状态中必然有一个点是必败的。那么也就是说,那么必败的点的出度必然为0。
为什么出度为0就是必败呢。因为对于目前的这个状态,他可以往前面转移,假定他有k种转移方法,那么他的出度就是k。那么,我们定义目前的状态是必胜态,那么他转移出去的必然都是必败态。所以,假如说那个是必胜态,那么我们就对k--。如果k是0了,那么表示转移出去的只有必胜态了,所以当前的状态只能是必败态
感觉理解起来还是简单的^0^,开心
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//看看会不会爆int!数组会不会少了一维! //取物问题一定要小心先手胜利的条件 #include <bits/stdc++.h> using namespace std; #pragma comment(linker,"/STACK:102400000,102400000") #define LL long long #define ALL(a) a.begin(), a.end() #define pb push_back #define mk make_pair #define fi first #define se second #define haha printf("haha ") const int maxn = 7000 + 5; int dp[2][maxn]; int cnt[2][maxn]; vector<int> ve[2]; int n, k; void solve(){ queue<pair<int ,int> > que; memset(dp, -1, sizeof(dp)); dp[0][0] = 0, dp[1][0] = 0;///0为必败,1为必胜 que.push(mk(0, 0)); que.push(mk(1, 0)); while (!que.empty()){ pair<int, int> p = que.front(); que.pop(); int x = p.fi, y = p.se; if (dp[x][y] == 0){ for (int i = 0; i < ve[x ^ 1].size(); i++){ int nx = x ^ 1, ny = (p.se + n - ve[x^1][i]) % n; if (dp[nx][ny] == -1){ dp[nx][ny] = 1; que.push(mk(nx, ny)); } } } else if (dp[x][y] == 1){ for (int i = 0; i < ve[x ^ 1].size(); i++){ int nx = x ^ 1, ny = (p.se + n - ve[x^1][i]) % n; cnt[nx][ny]--; if (cnt[nx][ny] == 0 && dp[nx][ny] == -1){ dp[nx][ny] = 0; que.push(mk(nx, ny)); } } } } for (int i = 0; i < 2; i++){ for (int j = 1; j < n; j++){ if (dp[i][j] == -1) printf("Loop "); else if (dp[i][j] == 0) printf("Lose "); else printf("Win "); } cout << endl; } } int main(){ cin >> n >> k; for (int i = 1; i < n; i++) cnt[0][i] = k; while (k--){ int u; cin >> u; ve[0].pb(u); } cin >> k; for (int i = 1; i < n; i++) cnt[1][i] = k; while (k--){ int u; cin >> u; ve[1].pb(u); } solve(); return 0; }