【SPOJ-PORTALUN】Portal【SG函数】

来源:互联网 发布:淘宝号怎么升级心快 编辑:程序博客网 时间:2024/05/18 22:55

题意:

给出一个无向图,每个点有编号,但是只能从大的编号的点走向小的编号的点。

给出一些人的初始位置。

对于其中一个人,你先走一步,对手再走一步。

无法走就输了。

问你是否赢。


人生中第一道自己推出+写对的博弈论的题...


题面是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;}


0 0
原创粉丝点击