题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3446
题意:有一个棋盘,一个king,给出走法,有一些地方能走,一些不能走,两个人移动king,不能移动者输,问先手是否能赢
思路:先将每个能连的点连接起来,先去掉king算最大匹配,再算上king算最大匹配(一定是大于等于前者的)
当大于前者时,先手一定赢,因为说明有一条以king为起点的增广路,只要沿着这条路走,后手就无路可走了。
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn=250; bool g[maxn][maxn],inque[maxn],inpath[maxn]; bool inhua[maxn]; int st,ed,newbase,ans,n; int base[maxn],pre[maxn],match[maxn]; int head,tail,que[maxn]; char s[maxn][maxn]; int dx[25]={-2,-2,-2,-2,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,2,2,2,2}; int dy[25]={-2,-1,1,2,-2,-1,0,1,2,-1,1,-2,-1,0,1,2,-2,-1,1,2}; //走法 void Push(int u) { que[tail]=u; tail++; inque[u]=1; } int Pop() { int res=que[head]; head++; return res; } int lca(int u,int v)//寻找公共花祖先 { memset(inpath,0,sizeof(inpath)); while(1) { u=base[u]; inpath[u]=1; if(u==st) break; u=pre[match[u]]; } while(1) { v=base[v]; if(inpath[v]) break; v=pre[match[v]]; } return v; } void reset(int u)//缩环 { int v; while(base[u]!=newbase) { v=match[u]; inhua[base[u]]=inhua[base[v]]=1; u=pre[v]; if(base[u]!=newbase) pre[u]=v; } } void contract(int u,int v)// { newbase=lca(u,v); memset(inhua,0,sizeof(inhua)); reset(u); reset(v); if(base[u]!=newbase) pre[u]=v; if(base[v]!=newbase) pre[v]=u; for(int i=1;i<=n;i++) { if(inhua[base[i]]){ base[i]=newbase; if(!inque[i]) Push(i); } } } void findaug() { memset(inque,0,sizeof(inque)); memset(pre,0,sizeof(pre)); for(int i=1;i<=n;i++)//并查集 base[i]=i; head=tail=1; Push(st); ed=0; while(head<tail) { int u=Pop(); for(int v=1;v<=n;v++) { if(g[u][v]&&(base[u]!=base[v])&&match[u]!=v) { if(v==st||(match[v]>0)&&pre[match[v]]>0)//成环 contract(u,v); else if(pre[v]==0) { pre[v]=u; if(match[v]>0) Push(match[v]); else//找到增广路 { ed=v; return ; } } } } } } void aug() { int u,v,w; u=ed; while(u>0) { v=pre[u]; w=match[v]; match[v]=u; match[u]=v; u=w; } } int edmonds()//匹配 { int res=0; memset(match,0,sizeof(match)); for(int u=1;u<=n;u++) { if(match[u]==0) { st=u; findaug();//以st开始寻找增广路 if(ed>0) aug();//找到增广路 重新染色,反向 } } for(int i=1;i<=n;i++) if(match[i]!=0) res++; return res; } int main() { int t,k=0,r,c,cnt; scanf("%d",&t); while(t--) { memset(g,0,sizeof(g)); scanf("%d%d",&r,&c); for(int i=1;i<=r;i++) { getchar(); for(int j=1;j<=c;j++) scanf("%c",&s[i][j]); } for(int i=1;i<=r;i++) { for(int j=1;j<=c;j++) { if(s[i][j]!='#') { for(int k=0;k<20;k++) { int x=i+dx[k]; int y=j+dy[k]; if(x>=1&&x<=r&&y>=1&&y<=c&&s[x][y]!='#')//建图 { g[(i-1)*c+j][(x-1)*c+y]=1; g[(x-1)*c+y][(i-1)*c+j]=1; } } } if(s[i][j]=='K') cnt=(i-1)*c+j; } } n=r*c; int num=edmonds(); printf("Case #%d: daizhenyang ",++k); for(int i=1;i<=n;i++)//去掉king if(g[cnt][i]) g[cnt][i]=g[i][cnt]=0; // for(int i=1;i<=n;i++) // if(match[i]!=0) // cout<<i<<"****"<<match[i]<<endl; if(num==edmonds()) printf("lose "); else printf("win "); } return 0; }