hdu3446 daizhenyang's chess,一般图匹配

hdu3446 daizhenyang's chess
一般图匹配问题。
将所有'.'的点之间可到达的建边。求最大匹配。

最后将K点加入,找增广路。
如果找到增广路,先手胜,否则后手胜。

证明比较容易(如果找到增广路,则先手可以找到一个包含偶数个点也就是奇数个边的路径,这样最后一次走的还是先手)。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define Maxn 250

int set[Maxn];
int vis[Maxn],next[Maxn],mark[Maxn],spouse[Maxn];
int q[Maxn*2],head,tail;
int n;
int tn,tm;
int tlev=0;
int di[]={-2,-2,-2,-2,-1,-1,-1,-1,-1,0,0,1,1,1,1,1,2,2,2,2};
int dj[]={-2,-1,1,2,-2,-1,0,1,2,-1,1,-2,-1,0,1,2,-2,-1,1,2};

int fi[Maxn],ne[Maxn*Maxn],to[Maxn*Maxn],te;


void addedge(int u,int v){
    //printf(" %d %d\n",u,v);
    te++;ne[te]=fi[u];fi[u]=te;to[te]=v;}

int findset(int x){return x==set[x]?x:set[x]=findset(set[x]);}
void mergeset(int x,int y){x=findset(x);y=findset(y);if (x!=y) set[x]=y;}

int findlca(int u,int v){
    tlev++;
    while(1){
        if (u!=-1){
            u=findset(u);
            if (vis[u]==tlev) return u;
            vis[u]=tlev;
            if (spouse[u]!=-1) u=next[spouse[u]];
            else u=-1;
        }
        swap(u,v);
    }
}

void group(int a,int p){
    int b,c;
    while(a!=p){
        b=spouse[a];c=next[b];
        if (findset(c)!=p) next[c]=b;
        if (mark[b]==2) mark[q[tail++]=b]=1;
        if (mark[c]==2) mark[q[tail++]=c]=1;
        mergeset(a,b);
        mergeset(b,c);
        a=c;
    }
}


void findaugment(int s){
    int i,j,k,l,e,u,v,p;
    for(i=1;i<=n;++i){
        next[i]=-1;set[i]=i;mark[i]=0;vis[i]=-1;
    }
    q[head=0]=s;tail=1;mark[s]=1;
    while(head<tail){
        if (spouse[s]!=-1) break;
        u=q[head++];
        for(e=fi[u];~e;e=ne[e]){
            v=to[e];
            if (spouse[u]!=v&&findset(u)!=findset(v)&&mark[v]!=2){
                if (mark[v]==1){
                    p=findlca(u,v);
                    if (findset(u)!=p) next[u]=v;
                    if (findset(v)!=p) next[v]=u;
                    group(u,p);
                    group(v,p);
                }
                else if (spouse[v]==-1){
                    next[v]=u;
                    for(j=v;j!=-1;){
                        k=next[j];
                        l=spouse[k];
                        spouse[k]=j;spouse[j]=k;
                        j=l;
                    }
                    break;
                }
                else{ //spouse[v]!=-1
                    next[v]=u;
                    mark[q[tail++]=spouse[v]]=1;
                    mark[v]=2;
                }
            }
        }
    }
}

int work(){
    int i,ret;
    for(i=1;i<=n;++i) spouse[i]=-1;
    for(i=1;i<=n;++i)if (spouse[i]==-1) findaugment(i);

    ret=0;
    for(i=1;i<=n;++i)
        if (~spouse[i]) ret++;
    return ret;
}

int abs(int x){return x<0?-x:x;}


char s[20][20];
int g[20][20];

void makegraph(int i,int j,int func){
    int d,vi,vj;
    for(d=0;d<20;++d){
        vi=i+di[d];
        vj=j+dj[d];
        if (vi<0||vi>=tn||vj<0||vj>=tm) continue;
        if (g[vi][vj]==1) {
            addedge(i*tm+j+1,vi*tm+vj+1);
            if (func==2) addedge(vi*tm+vj+1,i*tm+j+1);
        }
    }
}


int main(){
    int i,j,tcas,cas,sti,stj;
    scanf("%d",&tcas);
    for(cas=1;cas<=tcas;++cas){
        scanf("%d%d",&tn,&tm);
        n=tn*tm;
        for(i=0;i<tn;++i){
            scanf("%s",s[i]);
        }

        queue<int> qi,qj;
        memset(g,0,sizeof(g));
        for(i=0;i<tn;++i){
            for(j=0;j<tm;++j){
                if (s[i][j]=='K') {sti=i;stj=j;}
                else if(s[i][j]=='.') {qi.push(i);qj.push(j);g[i][j]=1;}
            }
        }
        memset(fi,-1,sizeof(fi));
        te=0;
        while(!qi.empty()){
            i=qi.front();qi.pop();
            j=qj.front();qj.pop();
            makegraph(i,j,1);
        }

        work();
        makegraph(sti,stj,2);
        findaugment(sti*tm+stj+1);
        printf("Case #%d: ",cas);
        if (spouse[sti*tm+stj+1]!=-1){
            printf("daizhenyang win\n");
        }
        else printf("daizhenyang lose\n");
    }
    return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值