hdu3446 daizhenyang's chess 【一般图匹配】

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3446

题意:在一个R行C列的棋盘上,俩个人轮流移动一个棋子,每次可以向相邻的20个格子移动,走过的每个格子自能走一次。另外,某些各自一开始就固定了不能走。  无法移动者输。问:先手能否赢。 

分析:首先,忽略K点,将其他能相互移动的格子连边,求一次最大匹配,再将K点加入图中,若存在增广路,则先手赢,否则后手赢。

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define Mn 400
#define Mm 20005
#define mod 1000000007
#define CLR(a,b) memset((a),(b),sizeof((a)))
#define CPY(a,b) memcpy ((a), (b), sizeof((a)))
#pragma comment(linker, "/STACK:102400000,102400000")
#define ul u<<1
#define ur (u<<1)|1
using namespace std;
typedef long long ll;
struct edge {
    int v,next;
}e[Mm];
int tot,head[Mn];
void addedge(int u,int v) {
    e[tot].v=v;
    e[tot].next=head[u];
    head[u]=tot++;
}
int cnt,pre[Mn];
int findpre(int x) {
    return x==pre[x]?pre[x]:pre[x]=findpre(pre[pre[x]]);
}
void ol(int a,int b) {
    a=findpre(a);
    b=findpre(b);
    if(a!=b) pre[a]=b;
}
int lk[Mn],vis[Mn],mark[Mn],match[Mn],ne[Mn];
int lca(int x,int y) {
    static int t=0;t++;
    while(1) {
        if(x!=-1) {
            x=findpre(x);
            if(vis[x]==t) return x;
            vis[x]=t;
            if(match[x]!=-1) x=ne[match[x]];
            else x=-1;
        }
        swap(x,y);
    }

}
queue<int> q;
void group(int a,int p) {
    while(a!=p) {
        int b=match[a],c=ne[b];
        if(findpre(c)!=p) ne[c]=b;
        if(mark[b]==2) mark[b]=1,q.push(b);
        if(mark[c]==2) mark[c]=1,q.push(c);
        ol(a,b),ol(b,c);
        a=c;
    }
}
void aug(int s) {
    for(int i=0;i<=cnt;i++) {
        ne[i]=-1;
        pre[i]=i;
        mark[i]=0;
        vis[i]=-1;
    }
    mark[s]=1;
    while(!q.empty()) q.pop();
    q.push(s);
    while((!q.empty())&&match[s]==-1) {
        int u=q.front();
        q.pop();
        for(int i=head[u];~i;i=e[i].next) {
            int v=e[i].v;
            if(match[u]==v||findpre(u)==findpre(v)||mark[v]==2) continue;
            if(mark[v]==1) {
                int r=lca(u,v);
                if(findpre(u)!=r) ne[u]=v;
                if(findpre(v)!=r) ne[v]=u;
                group(u,r);
                group(v,r);
            } else if(match[v]==-1) {
                ne[v]=u;
                for(int x=v;~x;) {
                    int y=ne[x];
                    int mv=match[y];
                    match[x]=y,match[y]=x;
                    x=mv;
                }
                break;
            } else {
                ne[v]=u;
                q.push(match[v]);
                mark[match[v]]=1;
                mark[v]=2;
            }
        }
    }
}
int mp[Mn][Mn],num[Mn][Mn];
void init() {
    tot=0;cnt=0;
    CLR(mp,0);
    CLR(head,-1);
    CLR(num,0);
}
int way[20][2]={-2,-2,-2,-1,-2,1,-2,2,-1,-2,-1,-1,-1,0,-1,1,-1,2,0,-1,0,1,1,-2,1,-1,1,0,1,1,1,2,2,-2,2,-1,2,1,2,2};
int main() {
    int t,n,m,kx,ky;
    char s[Mn];
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++) {
        scanf("%d%d",&n,&m);
        init();
        for(int i=0;i<n;i++) {
            scanf("%s",s);
            for(int j=0;j<m;j++) {
                if(s[j]=='K') {
                    kx=i,ky=j;
                }
                if(s[j]!='#') {
                    num[i][j]=++cnt;
                }
            }
        }
        for(int i=0;i<n;i++) {
            for(int j=0;j<m;j++) {
                if(!num[i][j]||(i==kx&&j==ky)) continue;
                for(int k=0;k<20;k++) {
                    int x=i+way[k][0];
                    int y=j+way[k][1];
                    if(x<0||x>=n||y<0||y>=m||(x==kx&&y==ky)||!num[x][y]) continue;
                    int u=num[i][j],v=num[x][y];
                    if(!mp[u][v]) {
                        addedge(u,v);
                        addedge(v,u);
                        mp[u][v]=mp[v][u]=1;
                    }
                }
            }
        }
        for(int i=1;i<=cnt;i++) match[i]=-1;
        for(int i=1;i<=cnt;i++) {
            if(match[i]==-1) {
                aug(i);
            }
        }
        for(int k=0;k<20;k++) {
            int x=kx+way[k][0];
            int y=ky+way[k][1];
            if(x<0||x>=n||y<0||y>=m||!num[x][y]) continue;
            int u=num[kx][ky],v=num[x][y];
            addedge(u,v);
            addedge(v,u);
        }
        aug(num[kx][ky]);
        printf("Case #%d: ",cas);
        if(match[num[kx][ky]]==-1) printf("daizhenyang lose\n");
        else printf("daizhenyang win\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值