【bzoj1443】【JSOI2009】【游戏game】【二分图博弈】

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。

题解:

         可以看出来是二分图博弈的模型。

         对图黑白染色之后判断先手是否必胜跑一遍匈牙利算法看一下是否存在完备匹配即可。

         如果存在一定是先手必败。

         对于第二问,我们可以记录一下左边没有匹配的点。

         从这些点开始dfs,从左到右走非匹配边,从右向左走匹配边。

         这样dfs到的左边的点就是答案。

         然后把二分图左右两边倒过来再做一遍即可。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 510
using namespace std;
int t1,t2,pos[N][N],x[4]={1,0,-1,0},y[4]={0,1,0,-1},bl[N*N];
int n,m,cnt,point[N*N],next[N*N],num,to[N*N];
bool f[N*N],vis[2][N*N],p[N*N];
char s[N][N];
void add(int x,int y){next[++cnt]=point[x];point[x]=cnt;to[cnt]=y;}
bool find(int x){
   for (int i=point[x];i;i=next[i])
    if (!f[to[i]]){
      f[to[i]]=1;
      if (!bl[to[i]]||find(bl[to[i]])){bl[to[i]]=x;return true;}
	}
   return false;
}
void dfs(int x,bool vis[]){
  vis[x]=1;
  for (int i=point[x];i;i=next[i])
    if (!vis[bl[to[i]]])dfs(bl[to[i]],vis);
}
int main(){
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;i++) scanf("%s",s[i]+1);
  for (int i=1;i<=n;i++)
   for (int j=1;j<=m;j++)
     if (s[i][j]=='.') pos[i][j]=++(i+j&1?t1:t2);
  for(int i=1;i<=n;i++)
   for (int j=1;j<=m;j++)
    if (s[i][j]=='.'&&(i+j&1))
     for (int k=0;k<4;k++){
      int px=i+x[k],py=j+y[k];
	  if (px<=0||py<=0||px>n||py>m||s[px][py]=='#') continue;
	  add(pos[i][j],pos[px][py]);	    
    }
  for (int i=1;i<=t1;i++){
    memset(f,0,sizeof(f));
    if (find(i)) num++;else p[i]=1;
  }
  if (num==t1&&num==t2){printf("LOSE\n");return 0;}
  for (int i=1;i<=t1;i++) if (p[i]) dfs(i,vis[1]);
  memset(point,0,sizeof(point));
  memset(p,0,sizeof(p));
  memset(bl,0,sizeof(bl));
  for (int i=1;i<=n;i++)
   for (int j=1;j<=m;j++)
    if (s[i][j]=='.'&&((i+j)%2==0)){
      for (int k=0;k<4;k++){
        int px=i+x[k],py=j+y[k];
        if (px<=0||py<=0||px>n||py>m||s[px][py]=='#') continue;
	    add(pos[i][j],pos[px][py]);	    
      }
    }
  for (int i=1;i<=t2;i++){
    memset(f,0,sizeof(f));
    if (!find(i)) p[i]=1;
  }
  for (int i=1;i<=t2;i++) if (p[i]) dfs(i,vis[0]);
  printf("WIN\n"); 
  for (int i=1;i<=n;i++)
   for (int j=1;j<=m;j++){
    if ((i+j&1)&&(vis[1][pos[i][j]]))printf("%d %d\n",i,j);
    if (((i+j)%2==0)&&(vis[0][pos[i][j]])) printf("%d %d\n",i,j);
   }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值