一般图匹配习题

一般图匹配习题

0.P6113 【模板】一般图最大匹配

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
struct Blossom_Tree{
    vector<int>e[N];
    queue<int>q;
    int mh[N],pre[N],s[N],id[N],vis[N],n,num;
    void init(int nn){	//初始化 
        n=nn,num=0;
        for(int i=1;i<=n;i++) e[i].clear(),mh[i]=pre[i]=vis[i]=s[i]=id[i]=0;
    }
    void add(int u,int v){
        e[u].push_back(v),e[v].push_back(u);
    }
    int find(int x){ return s[x]==x?x:s[x]=find(s[x]);}
    int LCA(int x,int y){	//求LCA. 
    for(++num;;swap(x,y))
        if(x){
        if(id[x=find(x)]==num) return x;//在同一个奇环肯定能找到被编号的结点. 
        id[x]=num,x=pre[mh[x]];	//沿增广路径的非匹配边向上走直到找到祖先. 
    }
    }
    void blossom(int x,int y,int rt){	//缩点(开花)  O(n)
        while(find(x)!=rt){
            pre[x]=y,y=mh[x];
            if(vis[y]==2) vis[y]=1,q.push(y);//如果奇环上有2型结点 就变为1型结点加入队列. 
            if(find(x)==x) s[x]=rt;//只对根处理. 
            if(find(y)==y) s[y]=rt;
            x=pre[y];
        }
    }
    bool aug(int st){	//求增广路径.O(n) 
    for(int i=1;i<=n;i++) vis[i]=pre[i]=0,s[i]=i; 
    while(!q.empty()) q.pop();
    q.push(st),vis[st]=1;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(auto v:e[u]){
            if(find(u)==find(v)||vis[v]==2) continue;//如果是已经缩点了或者是偶环直接继续,没有影响. 
            if(!vis[v]){	//如果没有被染色 
                vis[v]=2,pre[v]=u;
                if(!mh[v]){	//如果未被匹配,就进行匹配 
                    for(int x=v,last;x;x=last)	//增广路取反 
                        last=mh[pre[x]],mh[x]=pre[x],mh[pre[x]]=x;
                    return 1;
                }
                vis[mh[v]]=1,q.push(mh[v]); //否则将v的匹配结点染色,加入队列. 
            }
            else{
             int rt=LCA(u,v);	//找到LCA ,然后进行缩点. 
             blossom(u,v,rt),blossom(v,u,rt);
            }
        }
    }
    return 0;
    }
    void solve(){
        int ans=0;
        for(int i=1;i<=n;i++) if(!mh[i]&&aug(i)) ans++;	//最大匹配. 
        printf("%d\n",ans);
        for(int i=1;i<=n;i++)
        	printf("%d ",mh[i]);
    }
}T;
int n,m;
int main(){
    scanf("%d%d",&n,&m);
    int tot=0;
    T.init(n);
    for(int i=1,u,v;i<=m;i++){
    	scanf("%d%d",&u,&v);
   		T.add(u,v);
    }
    T.solve();
    return 0;
}

1.牛客多校 I.1 or 2

思路:带花树+拆点建图。

求一般图匹配用带花树就可以解决,主要思想是将奇环用并查集隐式缩点,因为此题要从中选出若干条边进行匹配,所以我们可以每个结点分为 d e g [ u ] deg[u] deg[u]个结点,一个结点代表一度,这样满足完全匹配的要求,具体看代码。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
struct Blossom_Tree{
    vector<int>e[N];
    queue<int>q;
    int mh[N],pre[N],s[N],id[N],vis[N],n,num;
    void init(int nn){	//初始化 
        n=nn,num=0;
        for(int i=1;i<=n;i++) e[i].clear(),mh[i]=pre[i]=vis[i]=s[i]=id[i]=0;
    }
    void add(int u,int v){
        e[u].push_back(v),e[v].push_back(u);
    }
    int find(int x){ return s[x]==x?x:s[x]=find(s[x]);}
    int LCA(int x,int y){	//求LCA. 
    for(++num;;swap(x,y))
        if(x){
        if(id[x=find(x)]==num) return x;//在同一个奇环肯定能找到被编号的结点. 
        id[x]=num,x=pre[mh[x]];	//沿增广路径的非匹配边向上走直到找到祖先. 
    }
    }
    void blossom(int x,int y,int rt){	//缩点(开花)  O(n)
        while(find(x)!=rt){
            pre[x]=y,y=mh[x];
            if(vis[y]==2) vis[y]=1,q.push(y);//如果奇环上有2型结点 就变为1型结点加入队列. 
            if(find(x)==x) s[x]=rt;//只对根处理. 
            if(find(y)==y) s[y]=rt;
            x=pre[y];
        }
    }
    bool aug(int st){	//求增广路径.O(n) 
    for(int i=1;i<=n;i++) vis[i]=pre[i]=0,s[i]=i; 
    while(!q.empty()) q.pop();
    q.push(st),vis[st]=1;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(auto v:e[u]){
            if(find(u)==find(v)||vis[v]==2) continue;//如果是已经缩点了或者是偶环直接继续,没有影响. 
            if(!vis[v]){	//如果没有被染色 
                vis[v]=2,pre[v]=u;
                if(!mh[v]){	//如果未被匹配,就进行匹配 
                    for(int x=v,last;x;x=last)	//增广路取反 
                        last=mh[pre[x]],mh[x]=pre[x],mh[pre[x]]=x;
                    return 1;
                }
                vis[mh[v]]=1,q.push(mh[v]); //否则将v的匹配结点染色,加入队列. 
            }
            else{
             int rt=LCA(u,v);	//找到LCA ,然后进行缩点. 
             blossom(u,v,rt),blossom(v,u,rt);
            }
        }
    }
    return 0;
    }
    int cal(){
        int ans=0;
        for(int i=1;i<=n;i++) if(!mh[i]&&aug(i)) ans++;	//最大匹配. 
        return ans;
    }
}DHS;
int  d1[N],d2[N],n,m;
int main(){
    while(~scanf("%d%d",&n,&m)){
    int tot=0;
    for(int i=1,x;i<=n;i++){
          scanf("%d",&x);
          if(x==1) d1[i]=++tot,d2[i]=d1[i];
          else d1[i]=++tot,d2[i]=++tot;
    }
    DHS.init(2*m+tot);
    n=2*m+tot;
    for(int i=1,u,v;i<=m;i++){
    scanf("%d%d",&u,&v);
    int e1=++tot,e2=++tot;
        DHS.add(e1,e2);	//拆点建图. 
        DHS.add(e1,d1[u]);
        DHS.add(e1,d2[u]);
        DHS.add(e2,d1[v]);
        DHS.add(e2,d2[v]);
    }
    puts(DHS.cal()*2==tot?"Yes":"No");
    }
    return 0;
}

2.HDU:3551

上一题的升级版,度数变为多个,还是拆点建图就 O K OK OK了。
主要是注意一些代码的细节。

传送门

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
struct Blossom_Tree{
    vector<int>e[N];
    queue<int>q;
    int mh[N],pre[N],s[N],id[N],vis[N],n,num;
    void init(int nn){	//初始化 
        n=nn,num=0;
        for(int i=1;i<=n;i++) e[i].clear(),mh[i]=pre[i]=vis[i]=s[i]=id[i]=0;
    }
    void add(int u,int v){
        e[u].push_back(v),e[v].push_back(u);
    }
    int find(int x){ return s[x]==x?x:s[x]=find(s[x]);}
    int LCA(int x,int y){	//求LCA. 
    for(++num;;swap(x,y))
        if(x){
        if(id[x=find(x)]==num) return x;//在同一个奇环肯定能找到被编号的结点. 
        id[x]=num,x=pre[mh[x]];	//沿增广路径的非匹配边向上走直到找到祖先. 
    }
    }
    void blossom(int x,int y,int rt){	//缩点(开花)  O(n)
        while(find(x)!=rt){
            pre[x]=y,y=mh[x];
            if(vis[y]==2) vis[y]=1,q.push(y);//如果奇环上有2型结点 就变为1型结点加入队列. 
            if(find(x)==x) s[x]=rt;//只对根处理. 
            if(find(y)==y) s[y]=rt;
            x=pre[y];
        }
    }
    bool aug(int st){	//求增广路径.O(n) 
    for(int i=1;i<=n;i++) vis[i]=pre[i]=0,s[i]=i; 
    while(!q.empty()) q.pop();
    q.push(st),vis[st]=1;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(auto v:e[u]){
            if(find(u)==find(v)||vis[v]==2) continue;//如果是已经缩点了或者是偶环直接继续,没有影响. 
            if(!vis[v]){	//如果没有被染色 
                vis[v]=2,pre[v]=u;
                if(!mh[v]){	//如果未被匹配,就进行匹配 
                    for(int x=v,last;x;x=last)	//增广路取反 
                        last=mh[pre[x]],mh[x]=pre[x],mh[pre[x]]=x;
                    return 1;
                }
                vis[mh[v]]=1,q.push(mh[v]); //否则将v的匹配结点染色,加入队列. 
            }
            else{
             int rt=LCA(u,v);	//找到LCA ,然后进行缩点. 
             blossom(u,v,rt),blossom(v,u,rt);
            }
        }
    }
    return 0;
    }
    int cal(){
        int ans=0;
        for(int i=1;i<=n;i++) if(!mh[i]&&aug(i)) ans++;	//最大匹配. 
        return ans;
    }
}T;
int  U[N],V[N],n,m,deg[N],g[N][N];
int main(){
	int t;
	scanf("%d",&t);
	for(int kase=1;kase<=t;kase++){
    scanf("%d%d",&n,&m);
    int tot=0;
    for(int i=1,x;i<=m;i++){
        	scanf("%d%d",&U[i],&V[i]);
    }
    int sum=n;
   	for(int i=1;i<=n;i++){
   		scanf("%d",&deg[i]),sum+=deg[i];
   		for(int j=1;j<=deg[i];j++) g[i][j]=++tot;
	}
	T.init(tot+2*m);	重要需要清空数组,不然会TLE. 
	for(int i=1;i<=m;i++){	//拆点建图. 
		for(int j=1;j<=deg[U[i]];j++) T.add(g[U[i]][j],tot+1);
		for(int j=1;j<=deg[V[i]];j++) T.add(g[V[i]][j],tot+2);
		T.add(tot+1,tot+2);
		tot+=2; 
	}
	printf("Case %d: ",kase); 
    puts(T.cal()*2==tot?"YES":"NO");
    }
    return 0;
}

3.ZOJ3316.Game

传送门

思路:表面看是个博弈,其实是一般图完美匹配,显然对于先手走的每一个点如果后手都能找到对应匹配的点就保证能赢,否则输。所以把每个距离小于等于 L L L的连边,然后用带花树跑一般图完美匹配即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
struct Blossom_Tree{
    vector<int>e[N];
    queue<int>q;
    int mh[N],pre[N],s[N],id[N],vis[N],n,num;
    void init(int nn){	//初始化 
        n=nn,num=0;
        for(int i=1;i<=n;i++) e[i].clear(),mh[i]=pre[i]=vis[i]=s[i]=id[i]=0;
    }
    void add(int u,int v){
        e[u].push_back(v),e[v].push_back(u);
    }
    int find(int x){ return s[x]==x?x:s[x]=find(s[x]);}
    int LCA(int x,int y){	//求LCA. 
    for(++num;;swap(x,y))
        if(x){
        if(id[x=find(x)]==num) return x;//在同一个奇环肯定能找到被编号的结点. 
        id[x]=num,x=pre[mh[x]];	//沿增广路径的非匹配边向上走直到找到祖先. 
    }
    }
    void blossom(int x,int y,int rt){	//缩点(开花)  O(n)
        while(find(x)!=rt){
            pre[x]=y,y=mh[x];
            if(vis[y]==2) vis[y]=1,q.push(y);//如果奇环上有2型结点 就变为1型结点加入队列. 
            if(find(x)==x) s[x]=rt;//只对根处理. 
            if(find(y)==y) s[y]=rt;
            x=pre[y];
        }
    }
    bool aug(int st){	//求增广路径.O(n) 
    for(int i=1;i<=n;i++) vis[i]=pre[i]=0,s[i]=i; 
    while(!q.empty()) q.pop();
    q.push(st),vis[st]=1;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(auto v:e[u]){
            if(find(u)==find(v)||vis[v]==2) continue;//如果是已经缩点了或者是偶环直接继续,没有影响. 
            if(!vis[v]){	//如果没有被染色 
                vis[v]=2,pre[v]=u;
                if(!mh[v]){	//如果未被匹配,就进行匹配 
                    for(int x=v,last;x;x=last)	//增广路取反 
                        last=mh[pre[x]],mh[x]=pre[x],mh[pre[x]]=x;
                    return 1;
                }
                vis[mh[v]]=1,q.push(mh[v]); //否则将v的匹配结点染色,加入队列. 
            }
            else{
             int rt=LCA(u,v);	//找到LCA ,然后进行缩点. 
             blossom(u,v,rt),blossom(v,u,rt);
            }
        }
    }
    return 0;
    }
    void solve(){
        int ans=0;
        for(int i=1;i<=n;i++) if(!mh[i]&&aug(i)) ans++;	//最大匹配. 
        puts(ans*2==n?"YES":"NO");
    }
}T;
int n,m,x[N],y[N],L;
bool check(int i,int j){
	return abs(x[i]-x[j])+abs(y[i]-y[j])<=L;
}
int main(){
    while(~scanf("%d",&n)){
    int tot=0;
    for(int i=1;i<=n;i++)
        scanf("%d%d",&x[i],&y[i]);
    scanf("%d",&L);
    T.init(n);
    for(int i=1;i<=n;i++)
    	for(int j=i+1;j<=n;j++){
    		  if(check(i,j))  
			  T.add(i,j);
		}
    T.solve();
	}
    return 0;
}

下面是邻接矩阵版。其实还可以再写个前向星版,这里就不写了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
int g[N][N];
struct Blossom_Tree{
    queue<int>q;
    int mh[N],pre[N],s[N],id[N],vis[N],n,num;
    void init(int nn){	//初始化 
        n=nn,num=0;
        for(int i=1;i<=n;i++) mh[i]=pre[i]=vis[i]=s[i]=id[i]=0;
        mst(g);
    }
    void add(int u,int v){
        g[u][v]=g[v][u]=1;
    }
    int find(int x){ return s[x]==x?x:s[x]=find(s[x]);}
    int LCA(int x,int y){	//求LCA. 
    for(++num;;swap(x,y))
        if(x){
        if(id[x=find(x)]==num) return x;//在同一个奇环肯定能找到被编号的结点. 
        id[x]=num,x=pre[mh[x]];	//沿增广路径的非匹配边向上走直到找到祖先. 
    }
    }
    void blossom(int x,int y,int rt){	//缩点(开花)  O(n)
        while(find(x)!=rt){
            pre[x]=y,y=mh[x];
            if(vis[y]==2) vis[y]=1,q.push(y);//如果奇环上有2型结点 就变为1型结点加入队列. 
            if(find(x)==x) s[x]=rt;//只对根处理. 
            if(find(y)==y) s[y]=rt;
            x=pre[y];
        }
    }
    bool aug(int st){	//求增广路径.O(n) 
    for(int i=1;i<=n;i++) vis[i]=pre[i]=0,s[i]=i; 
    while(!q.empty()) q.pop();
    q.push(st),vis[st]=1;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int v=1;v<=n;v++)
			if(g[u][v])
		{
            if(find(u)==find(v)||vis[v]==2) continue;//如果是已经缩点了或者是偶环直接继续,没有影响. 
            if(!vis[v]){	//如果没有被染色 
                vis[v]=2,pre[v]=u;
                if(!mh[v]){	//如果未被匹配,就进行匹配 
                    for(int x=v,last;x;x=last)	//增广路取反 
                        last=mh[pre[x]],mh[x]=pre[x],mh[pre[x]]=x;
                    return 1;
                }
                vis[mh[v]]=1,q.push(mh[v]); //否则将v的匹配结点染色,加入队列. 
            }
            else{
             int rt=LCA(u,v);	//找到LCA ,然后进行缩点. 
             blossom(u,v,rt),blossom(v,u,rt);
            }
        }
    }
    return 0;
    }
    void solve(){
        int ans=0;
        for(int i=1;i<=n;i++) if(!mh[i]&&aug(i)) ans++;	//最大匹配. 
        puts(ans*2==n?"YES":"NO");
    }
}T;
int n,m,x[N],y[N],L;
bool check(int i,int j){
	return abs(x[i]-x[j])+abs(y[i]-y[j])<=L;
}
int main(){
    while(~scanf("%d",&n)){
    int tot=0;
    for(int i=1;i<=n;i++)
        scanf("%d%d",&x[i],&y[i]);
    scanf("%d",&L);
    T.init(n);
    for(int i=1;i<=n;i++)
    	for(int j=i+1;j<=n;j++){
    		  if(check(i,j))  
			  T.add(i,j);
		}
    T.solve();
	}
    return 0;
}

URAL.1099

思路:显然是一般图最大匹配问题,注意一下不能边匹配边存答案,因为此时的匹配不代表最终还是这两个点匹配,所以要跑一边后再取找匹配的对应结点。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
int g[N][N];
struct Blossom_Tree{
    queue<int>q;
    int mh[N],pre[N],s[N],id[N],vis[N],n,num;
    void init(int nn){	//初始化 
        n=nn,num=0;
        for(int i=1;i<=n;i++) mh[i]=pre[i]=vis[i]=s[i]=id[i]=0;
        mst(g);
    }
    void add(int u,int v){
        g[u][v]=g[v][u]=1;
    }
    int find(int x){ return s[x]==x?x:s[x]=find(s[x]);}
    int LCA(int x,int y){	//求LCA. 
    for(++num;;swap(x,y))
        if(x){
        if(id[x=find(x)]==num) return x;//在同一个奇环肯定能找到被编号的结点. 
        id[x]=num,x=pre[mh[x]];	//沿增广路径的非匹配边向上走直到找到祖先. 
    }
    }
    void blossom(int x,int y,int rt){	//缩点(开花)  O(n)
        while(find(x)!=rt){
            pre[x]=y,y=mh[x];
            if(vis[y]==2) vis[y]=1,q.push(y);//如果奇环上有2型结点 就变为1型结点加入队列. 
            if(find(x)==x) s[x]=rt;//只对根处理. 
            if(find(y)==y) s[y]=rt;
            x=pre[y];
        }
    }
    bool aug(int st){	//求增广路径.O(n) 
    for(int i=1;i<=n;i++) vis[i]=pre[i]=0,s[i]=i; 
    while(!q.empty()) q.pop();
    q.push(st),vis[st]=1;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int v=1;v<=n;v++)
			if(g[u][v])
		{
            if(find(u)==find(v)||vis[v]==2) continue;//如果是已经缩点了或者是偶环直接继续,没有影响. 
            if(!vis[v]){	//如果没有被染色 
                vis[v]=2,pre[v]=u;
                if(!mh[v]){	//如果未被匹配,就进行匹配 
                    for(int x=v,last;x;x=last)	//增广路取反 
                        last=mh[pre[x]],mh[x]=pre[x],mh[pre[x]]=x;
                    return 1;
                }
                vis[mh[v]]=1,q.push(mh[v]); //否则将v的匹配结点染色,加入队列. 
            }
            else{
             int rt=LCA(u,v);	//找到LCA ,然后进行缩点. 
             blossom(u,v,rt),blossom(v,u,rt);
            }
        }
    }
    return 0;
    }
    void solve(){
        int cnt=0;
        for(int i=1;i<=n;i++) 
		if(!mh[i]&&aug(i)) cnt++;
        printf("%d\n",cnt*2);
    	for(int i=1;i<=n;i++){
    		if(mh[i]) printf("%d %d\n",i,mh[i]),mh[mh[i]]=0;
		}
    }
}T;
int n,m;
int main(){
    while(~scanf("%d",&n)){
    int tot=0;
    T.init(n);
    int u,v;
    while(~scanf("%d%d",&u,&v)){
    	 T.add(u,v);
	}
    T.solve();
	}
    return 0;
}

HDU.3446.daizhenyang’s chess

思路:显然是一般图匹配问题,我们只需判断有 K i n g King King这个结点时的最大匹配和没 K i n g King King这个结点时的最大匹配是否相同,如果相同 ,说明没有从 K i n g King King开始的一条增广路径,即 K K K不在最大匹配里,则 K i n g King King是非匹配点,先手只能走到匹配点,而后手永远 可以走匹配边,所以后手胜,反之 K i n g King King在最大匹配里,说明 K i n g King King可以作为匹配点,则先手每次都走匹配边走到非匹配点,而非匹配点只能走到匹配点,这样先手永远可以走匹配边,先手胜 。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
int g[N][N];
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};
struct Blossom_Tree{
    queue<int>q;
    int mh[N],pre[N],s[N],id[N],vis[N],n,num;
    void init(int nn){	//初始化 
        n=nn,num=0;
        for(int i=1;i<=n;i++) mh[i]=pre[i]=vis[i]=s[i]=id[i]=0;
        mst(g);
    }
    void add(int u,int v){
        g[u][v]=g[v][u]=1;
    }
    int find(int x){ return s[x]==x?x:s[x]=find(s[x]);}
    int LCA(int x,int y){	//求LCA. 
    for(++num;;swap(x,y))
        if(x){
        if(id[x=find(x)]==num) return x;//在同一个奇环肯定能找到被编号的结点. 
        id[x]=num,x=pre[mh[x]];	//沿增广路径的非匹配边向上走直到找到祖先. 
    }
    }
    void blossom(int x,int y,int rt){	//缩点(开花)  O(n)
        while(find(x)!=rt){
            pre[x]=y,y=mh[x];
            if(vis[y]==2) vis[y]=1,q.push(y);//如果奇环上有2型结点 就变为1型结点加入队列. 
            if(find(x)==x) s[x]=rt;//只对根处理. 
            if(find(y)==y) s[y]=rt;
            x=pre[y];
        }
    }
    bool aug(int st){	//求增广路径.O(n) 
    for(int i=1;i<=n;i++) vis[i]=pre[i]=0,s[i]=i; 
    while(!q.empty()) q.pop();
    q.push(st),vis[st]=1;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int v=1;v<=n;v++)
			if(g[u][v])
		{
            if(find(u)==find(v)||vis[v]==2) continue;//如果是已经缩点了或者是偶环直接继续,没有影响. 
            if(!vis[v]){	//如果没有被染色 
                vis[v]=2,pre[v]=u;
                if(!mh[v]){	//如果未被匹配,就进行匹配 
                    for(int x=v,last;x;x=last)	//增广路取反 
                        last=mh[pre[x]],mh[x]=pre[x],mh[pre[x]]=x;
                    return 1;
                }
                vis[mh[v]]=1,q.push(mh[v]); //否则将v的匹配结点染色,加入队列. 
            }
            else{
             int rt=LCA(u,v);	//找到LCA ,然后进行缩点. 
             blossom(u,v,rt),blossom(v,u,rt);
            }
        }
    }
    return 0;
    }
    int solve(){
        int ans=0;
        for(int i=1;i<=n;i++) 
		if(!mh[i]&&aug(i)) ans++;
        return ans;
    }
}T;
int r,c,t;
int Turn(int x,int y){
	return (x-1)*c+y;
} 
char mp[N][N];
int main(){
   scanf("%d",&t);
    for(int kase=1;kase<=t;kase++){
    	scanf("%d%d",&r,&c);
    	for(int i=1;i<=r;i++)
    		scanf("%s",mp[i]+1);
    	int K_X,K_Y; 
    	T.init(r*c);
    	for(int i=1;i<=r;i++)
    		for(int j=1;j<=c;j++){
    				if(mp[i][j]=='K') K_X=i,K_Y=j;
    				if(mp[i][j]=='#') continue;
    			for(int k=0;k<20;k++){
    				int X=i+dx[k],Y=j+dy[k];
    				if(X>=1&&X<=r&&Y>=1&&Y<=c&&mp[X][Y]!='#'){
    					int u=Turn(i,j),v=Turn(X,Y);
    					 T.add(u,v);
					}
				}
			}
		int ans=T.solve(),K=Turn(K_X,K_Y);
		for(int i=1;i<=r*c;i++)
			if(g[K][i]) g[K][i]=g[i][K]=0;
		mst(T.mh),mst(T.id);
    	printf("Case #%d: daizhenyang ",kase);
    	puts(ans!=T.solve()?"win":"lose");
	}
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酷酷的Herio

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值