【SPOJ-PORTALUN】Portal【SG函数】

题意:

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

给出一些人的初始位置。

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

无法走就输了。

问你是否赢。


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


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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值