题意:
给出一个无向图,每个点有编号,但是只能从大的编号的点走向小的编号的点。
给出一些人的初始位置。
对于其中一个人,你先走一步,对手再走一步。
无法走就输了。
问你是否赢。
人生中第一道自己推出+写对的博弈论的题...
题面是portal,也算是我比较喜欢的游戏了。
我的思路是,变为一个有向图,从小的向大的连边,每个点记录入度。
显然入度为0的点的SG函数等于0。
类似拓扑排序,将入度为0的点放入队列,每次更新与它相邻的点(这里只会更新编号比它大的点)。
这样就可以求出所有点的SG函数。
我是对每个点建立了set,这样求mex比较方便...后来发现可以用二维bool数组,但是SPOJ内存给的大,就用STL了。
注意,有可能有自环,需要特判。
#include <cstdio>
#include <iostream>
#include <set>
using namespace std;
typedef set<int>::iterator siit;
const int maxn = 1005, maxm = 500005, maxq = 10000;
int n, m, k, head[maxn], cnt, SG[maxn], du[maxn], q[maxq];
set<int> st[maxn];
struct _edge {
int v, next;
} g[maxm];
inline void add(int u, int v) {
g[cnt] = (_edge) {v, head[u]};
head[u] = cnt++;
}
int main() {
while(scanf("%d%d", &n, &m) == 2) {
for(int i = 1; i <= n; i++) head[i] = SG[i] = -1, du[i] = 0, st[i].clear(); cnt = 0;
for(int i = 1; i <= m; i++) {
int u, v; scanf("%d%d", &u, &v);
u++; v++;
if(u == v) continue;
if(u > v) u ^= v ^= u ^= v;
du[v]++;
add(u, v);
}
int h = 0, t = 0;
for(int i = 1; i <= n; i++) if(!du[i]) SG[i] = 0, q[t++] = i;
while(h != t) {
int u = q[h++];
for(int i = head[u]; ~i; i = g[i].next) {
du[g[i].v]--;
st[g[i].v].insert(SG[u]);
if(!du[g[i].v]) {
int mex = 0;
for(siit it = st[g[i].v].begin(); it != st[g[i].v].end(); it++, mex++) if(*it != mex) break;
SG[g[i].v] = mex;
q[t++] = g[i].v;
}
}
}
scanf("%d", &k);
int ans = 0;
for(int i = 1; i <= k; i++) {
int x; scanf("%d", &x); x++;
ans ^= SG[x];
}
printf(ans ? "I win\n" : "I lose\n");
}
return 0;
}