1443: [JSOI2009]游戏Game
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1242 Solved: 573
[ Submit][ Status][ Discuss]
Description
Input
输入数据首先输入两个整数N,M,表示了迷宫的边长。 接下来N行,每行M个字符,描述了迷宫。
Output
若小AA能够赢得游戏,则输出一行"WIN",然后输出所有可以赢得游戏的起始位置,按行优先顺序输出 每行一个,否则输出一行"LOSE"(不包含引号)。
Sample Input
.##
...
#.#
Sample Output
2 3
3 2
HINT
对于100%的数据,有1≤n,m≤100。 对于30%的数据,有1≤n,m≤5。
Source
这就是二分图博弈啊...之前YJQ讲过, 当时感觉很清晰很简单, 今天考试做到NOI2011 D2T3突然发现全部忘完了, 话说NOI2011 D2T1道路修建是什么鬼...怎么还有这么水的题, 跟NOIP T1难度差不多...所以做完T1就开始无聊了, 开始打开OY的老年手机玩数独...于是乎就玩了2个小时.
我们把棋盘黑白染色, 直白点就是当前这点向上下左右(如果不是#的话)连边. 你这样, 你先跑一边二分图最大匹配, 你就会发现有个神奇的东西. 一共有两种情况.
其一, 如果完美匹配,AA必败. AA放在哪里都是匹配到的点, 那么YY走一步, 相当于就是走一条边, 从当前这个匹配点走到另一个匹配点, 一条边走完了. 好下一步AA走, 由于这条匹配边走完了 ,那么AA只能从当前点移向另外一点(点不重复走), 而那个点一定也是匹配点(因为完美匹配). 那YY就又可以走那个点的匹配边. 这样循环往复, YY可以一直走匹配边, 而AA只能从一个被走过的匹配边的末端一向另一条没走过的匹配边的开始, 供YY走匹配边. 一句话: AA在边与边之间忙碌奔波, 走到的新边却只能由YY来享用. 那这样一直就会走到AA无法从当前这条边走到下一条边去(都走过了), 那AA就输了. 所以完美匹配, AA必败.
其二, 如果非完美匹配. AA必胜. AA把点放在一个未匹配的点, 那么YY走一步一定会走到一个匹配点(要不然就是一个非匹配点走到一个非匹配点,那么这两个点明明有边可以连却没有连, 与最大匹配矛盾, 所以这步YY一定会走到一个匹配点). 那么接下来AA就可以一直走匹配边了...为什么? 假设AA走走完了一条边, YY移动到一个非匹配点. 那么这个时候矛盾就来了. 这个时候AA初始放的那个非匹配点, 就有边可以连, 然后一路走来的边重组, 最后一个跟YY此时的非匹配点相连, 这样就多了一个匹配, 与最大匹配矛盾, 所以YY走到的一定是一个匹配点. 这个地方画个图理解一下.
黑色边就是匹配了的边,AA一开始把棋子放在0这个非匹配点, YY走失配边(0, 1 - 2, 3 - 4, 5), AA走匹配边(1, 2, - 3, 4, - 6, 7), 如果走到4, 该YY走失配边(4, 5), 5如果未匹配的话, 那么很明显这就不是最大匹配, 出现了增广路, 因为我们只需要将4, 5连条边, 我们就会发现匹配数多了1, 比如下图, "重组"后的情况.
其实仔细看显然...但本菜鸡对匹配不大熟悉所以画了图才明白.
这样AA就可以一直走匹配边了, YY终会走到无法移动到另一条边的时候然后输掉.
综上, 完美匹配AA LOSE, 否则WIN.
最后输出那些点可以win. 其实就是哪些点不是一定在最大匹配里的点 我们从当前那些二分图左边未匹配点dfs, dfs到二分图左边的点都是答案, 因为本来未匹配的点可以将失配边转化成匹配边, 重新改一下连边去替换这些点. 这里可以自己画画图想一想.
#include<stdio.h>
#include<bitset>
#include<cstring>
#define clear(a) memset(a, false, sizeof(a))
using namespace std;
const int maxn = 205;
const int maxm = 100005;
char s[maxn];
bitset<maxm> vis;
bool ans[maxm], mark[maxn][maxn];
int cnt, h[maxm], match[maxm], n, m, nm, num, ret, q[maxm];
int dx[4] = {0, 1, -1, 0};
int dy[4] = {-1, 0, 0, 1};
struct edge{
int v, nxt;
}e[maxm * 2];
inline void add(int u,int v){
e[++num].v = v, e[num].nxt = h[u], h[u] = num;
e[++num].v = u, e[num].nxt = h[v], h[v] = num;
}
bool find(int u){
for(int i = h[u]; i; i = e[i].nxt)
if(!vis[e[i].v]){
int v = e[i].v;
vis[v] = true;
if(!match[v] || find(match[v])) return match[v] = u;
}
return false;
}
void dfs(int u){
ans[u] = true;
for(int i = h[u]; i; i = e[i].nxt){
int v = e[i].v;
if(match[v] && !ans[match[v]]) dfs(match[v]);
}
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i){
scanf("%s", s + 1);
for(int j = 1; j <= m; ++j)
if(s[j] == '.') mark[i][j] = true;
}
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
if(mark[i][j])
for(int k = 0; k < 4; ++k)
if(mark[i + dx[k]][j + dy[k]]) add((i - 1) * m + j, (i + dx[k] - 1) * m + j + dy[k] + n * m);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
if(mark[i][j]){
if(!find((i - 1) * m + j)) q[++ret] = (i - 1) * m + j;
vis = 0;
}
if(!ret) {puts("LOSE"); return 0;}
puts("WIN");
for(int i = 1; i <= ret; ++i) dfs(q[i]);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
if(ans[(i - 1) * m + j]) printf("%d %d\n", i, j);
}