BZOJ1443 [JSOI2009]游戏Game 【博弈论 + 二分图匹配】

题目链接

BZOJ1443

题解

既然是网格图,便可以二分染色
二分染色后发现,游戏路径是黑白交错的
让人想到匹配时的增广路
后手要赢【指移动的后手】,必须在一个与起点同色的地方终止

容易想到完全匹配的图先手是必胜的,因为完全匹配的图要么走到对面终止,要么从对面找一条非匹配边走回来,而由于是完全匹配,总能继续走下去,所以先手总能走到一个不同色点
于是乎对于一个匹配完的二分图,我们从一个未匹配的点出发,此时先手只能走未匹配边,而由于已经是匹配完毕,所以走到的点一定是已匹配的点,此时我们可以继续走到该点的匹配点,先手要么无法操作,要么继续走一条未匹配边
换言之,我们走的总是交错边,而交错边上的匹配情况是可以互换的,所以从一个未匹配的点出发能走到的同侧的点都是后手必胜的

也不知道为什么\(O(n^3)\)怎么能跑\(10^4\)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 10005,maxm = 1000005,INF = 1000000000;
inline int read(){
	int out = 0,flag = 1; char c = getchar();
	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
	return out * flag;
}
int N,M,n,cnt;
int h[maxn],ne;
struct EDGE{int to,nxt;}ed[maxm];
inline void build(int u,int v){
	ed[++ne] = (EDGE){v,h[u]}; h[u] = ne;
	ed[++ne] = (EDGE){u,h[v]}; h[v] = ne;
}
char s[105][105];
int id[105][105],c[maxn],x[maxn],y[maxn];
void dfs1(int u){
	Redge(u) if (c[to = ed[k].to] == -1)
		c[to] = c[u] ^ 1,dfs1(to);
}
int lk[maxn],vis[maxn],ok[maxn];
bool find(int u){
	Redge(u) if (!vis[to = ed[k].to]){
		vis[to] = true;
		if (!lk[to] || find(lk[to])){
			lk[to] = u;
			return true;
		}
	}
	return false;
}
void dfs(int u){
	if (ok[u]) return;
	ok[u] = true;
	Redge(u) if (lk[to = ed[k].to]) dfs(lk[to]);
}
inline bool cmp(const int& a,const int& b){
	return x[a] == x[b] ? y[a] < y[b] : x[a] < x[b];
}
int main(){
	N = read(); M = read();
	REP(i,N){
		scanf("%s",s[i] + 1);
		REP(j,M) if (s[i][j] == '.') id[i][j] = ++n,x[n] = i,y[n] = j;
	}
	REP(i,N) REP(j,M){
		if (!id[i][j]) continue;
		if (id[i + 1][j]) build(id[i][j],id[i + 1][j]);
		if (id[i][j + 1]) build(id[i][j],id[i][j + 1]);
	}
	REP(i,n) c[i] = -1;
	REP(i,n) if (c[i] == -1) c[i] = 0,dfs1(i);
	REP(i,n) if (c[i]) cls(vis),cnt += find(i);
	if (cnt * 2 == n){puts("LOSE"); return 0;}
	REP(i,n) if (!c[i]) lk[lk[i]] = i;
	REP(i,n) if (!lk[i]) dfs(i);
	puts("WIN");
	REP(i,n) if (ok[i]) printf("%d %d\n",x[i],y[i]);
	return 0;
}

posted @ 2018-07-01 18:41  Mychael  阅读(233)  评论(0编辑  收藏  举报