[BZOJ]1443 [JSOI2009]游戏Game 二分图+博弈

1443: [JSOI2009]游戏Game

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 1242   Solved: 573
[ Submit][ Status][ Discuss]

Description

Input

输入数据首先输入两个整数N,M,表示了迷宫的边长。 接下来N行,每行M个字符,描述了迷宫。

Output

若小AA能够赢得游戏,则输出一行"WIN",然后输出所有可以赢得游戏的起始位置,按行优先顺序输出 每行一个,否则输出一行"LOSE"(不包含引号)。

Sample Input

3 3
.##
...
#.#

Sample Output

WIN
2 3
3 2

HINT

对于100%的数据,有1≤n,m≤100。 对于30%的数据,有1≤n,m≤5。

Source

[ Submit][ Status][ Discuss]


HOME Back

这就是二分图博弈啊...之前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);
}




 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值