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;
}

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

时间: 2024-10-19 19:39:27

hdu3446 daizhenyang's chess 【一般图匹配】的相关文章

HDU 3446 daizhenyang&#39;s chess

http://acm.hdu.edu.cn/showproblem.php?pid=3446 题意:一个棋盘,有个KING,有一些能走的点,每次只能走到没走过的地方,没路可走的输,求先手是否必胜. 思路:先去掉KING的位置,只考虑其他的,如果这样求出的匹配数和加上king的匹配数一样,说明KING这个位置没有在匹配中,因此后手必胜,否则先手必胜,为什么? 可以思考一下,匹配的路径是一条:匹配,不匹配,匹配...不匹配,匹配,因此如果KING在匹配中,那最后一步也能够是先手走的. #includ

kuangbin带你飞 匹配问题 二分匹配 + 二分图多重匹配 + 二分图最大权匹配 + 一般图匹配带花树

二分匹配:二分图的一些性质 二分图又称作二部图,是图论中的一种特殊模型. 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图. 1.一个二分图中的最大匹配数等于这个图中的最小点覆盖数 König定理是一个二分图中很重要的定理,它的意思是,一个二分图中的最大匹配数等于这个图中的最小点覆盖数.如果你还不知道什么是最小点覆盖,我也在这里说一下:假如选

HDU 4687 Boke and Tsukkomi(一般图匹配|带花树)

比赛的时候刚开始看这题还以为是二分图匹配,后来才发现根本不是,因为该题存在长度为奇数的圈 .  比如1->2,2->3,3->1 . 所以该题要用一般图匹配,即带花树算法 . 比赛时抄的模板有地方抄错了,上述样例出现了死循环 .   赛后补题的时候用map去重却得不到正确答案,不知为何,暂放 ,下面给出一种正确解法. 细节参见代码: #include<cstdio> #include<cstring> #include<iostream> #inclu

HDU 4687 Boke and Tsukkomi 一般图匹配,带花树,思路,输出注意空行 难度:4

http://acm.hdu.edu.cn/showproblem.php?pid=4687 此题求哪些边在任何一般图极大匹配中都无用,对于任意一条边i,设i的两个端点分别为si,ti, 则任意一个极大匹配中都必然有si或ti至少一个点被匹配,当在图中去掉si,ti两个点时,匹配数会损失一个或两个. 如果损失两个,就说明在极大匹配中这两个点分别连接不同的边,于是边i是无用的 所以总体思路:一般图匹配求出最大匹配数cnt0,分别试着去掉每条边的端点,再次匹配,匹配数如果小于cnt0-1,则这条边无

URAL 1099. Work Scheduling 一般图匹配带花树

一般图匹配带花树模版题: 将奇环缩成圈(Blossom),然后找增广路..... 1099. Work Scheduling Time limit: 0.5 second Memory limit: 64 MB There is certain amount of night guards that are available to protect the local junkyard from possible junk robberies. These guards need to sche

[kuangbin带你飞]专题十 匹配问题 一般图匹配

过去做的都是二分图匹配 即 同一个集合里的点 互相不联通 但是如果延伸到一般图上去 求一个一般图的最大匹配 就要用带花树来解决 带花树模板 用来处理一个无向图上的最大匹配 看了一会还是不懂  抄了一遍kuangbin的模板熟悉了一下 还有一个一般图最大权匹配 保存下来了VFK菊苣的模板题代码当作板子 http://uoj.ac/submission/16359 但愿以后的比赛永远也遇不到 .. 遇到了也能抄对 .. 抄错了也能过 .. R ural1099 kuangbin模板 #include

一般图匹配

tutte矩阵 一般图的最大匹配数为tutte矩阵的秩(必为偶数)除2 求出最大匹配后加入(n-2*最大匹配数)个点与所有点连边,新图一定存在最大匹配 与新点匹配的点在原图中不与任何点匹配 求一张图的完美匹配时可尝试删除一条边,两个点看是否仍存在完美匹配 有结论两点i,j可能在最大匹配中当且仅当 A的逆矩阵[j][i]不为0且i,j有边相连 删除一条边时可以以第i行的第j列为主元,第j行的第i列为主元消元维护逆矩阵 复杂度O(n^3)常数巨大. #pragma GCC optimize(2) #

一般图匹配带花树

R - Work Scheduling Time Limit:500MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Practice URAL 1099 Appoint description: Description There is certain amount of night guards that are available to protect the local junkyard

HDU 1045 Fire Net(图匹配)

题目大意: 这个是以前做过的一道DFS题目,当时是完全暴力写的. 给你一个N代表是N*N的矩阵,矩阵内 ‘X’代表墙, ‘.’代表通道. 问这个矩阵内最多可以放几个碉堡, 碉堡不能在同一行或者同一列,除非他们中间有墙. 二分图做法思想:我们用行去匹配列,判断最大匹配数. 我们需要重新构图, 假如一行中 (  ..X..X.. ) 那么在这一行中我们其实是可以分割到三个不同的行(因为中间隔有X).然后对这个三个行进行编号.同理列也是一样的.当我们完全构好图后就可以做完全匹配了,其他的跟HDU 10