正题
题目链接:https://www.luogu.com.cn/problem/P4055
题目大意
n∗mn*mn∗m的网格有的不能走,走过的不能走。开始有一个棋子先手可以决定位置,然后后先手轮流走,不能走的就输了,求先手的必胜开始位置。
解题思路
我们将图二分图染色,然后可以走的两两连边。如果这张图有完全匹配,那么可以发现无论先手走到哪个点,后手可以顺着完全匹配的边走,因为这个边一定是先手没有走过的。
如果图没有完全匹配,考虑胜负情况。此时对于一个匹配来说,图上会有一些点删除后不会影响最大匹配,如果先手选择了这些不需要的点作为起点,那么后手无论走到哪先手就可以顺着最大匹配的方法走了。
现在问题是考虑如何求出所有最大匹配的不需要点,我们可以先跑一遍网络流。然后从源点出发走没有流完的边,这些边可以作为当前匹配没有匹配的边或者增广路,然后走到的所有左边的点就是答案。而汇点同理,走流完的边即可,走到的右边的点就是答案。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define p(x,y) ((x)-1)*m+(y)
using namespace std;
const int N=110*110,inf=2147483647/3;
const int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
struct node{int to,next,w;
}a[N<<3];
int n,m,s,t,tot=1,ls[N],dep[N],col[N];
bool v[110][110],flag,ans[N];char st[110];
queue<int> q;
void addl(int x,int y,int w){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;a[tot].w=0;return;
}
bool bfs(){while(!q.empty())q.pop();q.push(s);memset(dep,0,sizeof(dep));dep[s]=1;while(!q.empty()){int x=q.front();q.pop();for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(dep[y]||!a[i].w)continue;dep[y]=dep[x]+1;if(y==t)return 1;q.push(y);}}return 0;
}
int dinic(int x,int flow){int rest=0,k;if(x==t)return flow;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(dep[x]+1!=dep[y]||!a[i].w)continue;rest+=(k=dinic(y,min(a[i].w,flow-rest)));a[i].w-=k;a[i^1].w+=k;if(rest==flow)return flow;}if(!rest)dep[x]=0;return rest;
}
void dfs(int x,int c){if(dep[x])return;dep[x]=1;if(col[x]==c)ans[x]=flag=1;for(int i=ls[x];i;i=a[i].next)if(a[i].w==c)dfs(a[i].to,c);return;
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%s",st+1);for(int j=1;j<=m;j++)v[i][j]=(st[j]=='#');}s=0;t=p(n,m)+1;col[s]=col[t]=-1;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){if(!v[i][j]){if((i+j)&1)addl(p(i,j),t,1),col[p(i,j)]=0;else{addl(s,p(i,j),1),col[p(i,j)]=1;for(int k=0;k<4;k++){int x=i+dx[k],y=j+dy[k];if(x<1||y<1||x>n||y>m||v[x][y])continue;addl(p(i,j),p(x,y),1);}}}}while(bfs())dinic(s,inf);memset(dep,0,sizeof(dep)); dfs(s,1);dfs(t,0);if(!flag)return printf("LOSE")&0;printf("WIN\n");for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(ans[p(i,j)])printf("%d %d\n",i,j);return 0;
}