zoukankan      html  css  js  c++  java
  • 【BZOJ1443】游戏(二分图匹配,博弈论)

    【BZOJ1443】游戏(二分图匹配,博弈论)

    题面

    BZOJ

    题解

    很明显的二分图博弈问题。
    发现每次移动一定是从一个黑点到达一个白点,或者反过来。
    所以可以对于棋盘进行染色然后连边。
    考虑一下必胜策略。
    如果选择从一个匹配点开始走,
    另外一个人沿着匹配点走,那么就输了,因为匹配点不一定有出边了。
    如果选择从一个非匹配点开始走,
    另外一个人无论怎么走都只能走到一个匹配点(或者无路可走)
    如果另外一个人可以走到一个非匹配点,意味着这两个点可以匹配,所以不存在这种情况。
    那么,先手只需要沿着匹配边走就一定能够做到必胜。
    所以黑白染色之后连边,找到所有不一定在最大匹配中的点就好了。

    直接跑匈牙利非常慢啊(虽然能够过)
    匈牙利代码:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    #define ll long long
    #define RG register
    #define MAX 111
    inline int read()
    {
        RG int x=0,t=1;RG char ch=getchar();
        while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
        if(ch=='-')t=-1,ch=getchar();
        while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
        return x*t;
    }
    struct Line{int v,next;}e[MAX*MAX*4];
    int h[MAX*MAX],cnt=1;
    inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
    char g[MAX][MAX];
    int n,m,bh[MAX][MAX],tot;
    int match[MAX*MAX],vis[MAX*MAX],tim;
    int d[4][2]={0,1,1,0,-1,0,0,-1};
    bool ban[MAX*MAX];
    int ans;
    pair<int,int> p[MAX*MAX];
    bool dfs(int u)
    {
    	if(ban[u])return false;
    	for(int i=h[u];i;i=e[i].next)
    		if(vis[e[i].v]!=tim&&!ban[e[i].v])
    		{
    			vis[e[i].v]=tim;
    			if(!match[e[i].v]||dfs(match[e[i].v]))
    			{match[u]=e[i].v;match[e[i].v]=u;return true;}
    		}
    	return false;
    }
    int main()
    {
    	n=read();m=read();
    	for(int i=1;i<=n;++i)scanf("%s",g[i]+1);
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			if(g[i][j]!='#')bh[i][j]=++tot;
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			if(((i+j)&1)&&g[i][j]=='.')
    				for(int k=0;k<4;++k)
    				{
    					int x=i+d[k][0],y=j+d[k][1];
    					if(x<1||x>n||y<1||y>m||g[x][y]=='#')continue;
    					Add(bh[i][j],bh[x][y]);Add(bh[x][y],bh[i][j]);
    				}
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			if(((i+j)&1)&&g[i][j]=='.')
    				if(!match[bh[i][j]])++tim,dfs(bh[i][j]);
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			if(g[i][j]=='.')
    			{
    				if(!match[bh[i][j]]){p[++ans]=make_pair(i,j);continue;}
    				ban[bh[i][j]]=true;int nw=match[bh[i][j]];
    				match[bh[i][j]]=match[nw]=0;++tim;
    				if(dfs(nw))p[++ans]=make_pair(i,j);
    				else match[bh[i][j]]=nw,match[nw]=bh[i][j];
    				ban[bh[i][j]]=false;
    			}
    	puts(ans?"WIN":"LOSE");sort(&p[1],&p[ans+1]);
    	for(int i=1;i<=ans;++i)printf("%d %d
    ",p[i].first,p[i].second);
    	return 0;
    }
    

    跑网络流就会快很多。
    但是怎么判断一个点是否在不一定在最大匹配中呢?
    把所有和(S)相连的点(用还有剩余流量的边连接)全部扣下来,这些点一定满足条件。
    为什么呢?
    首先不在当前的这个匹配中的点一定会被计算。
    如果一个点在匹配中,但是他被连上了,那么一定是通过一个没有被匹配上的点,到达当前点的匹配点,在连回来的,这样子意味着可以交换匹配。
    (T)相连的点同理解决即可。
    这样子快很多。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    using namespace std;
    #define ll long long
    #define RG register
    #define MAX 111
    inline int read()
    {
        RG int x=0,t=1;RG char ch=getchar();
        while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
        if(ch=='-')t=-1,ch=getchar();
        while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
        return x*t;
    }
    struct Line{int v,next,w;}e[MAX*MAX*10];
    int h[MAX*MAX],cnt=2;
    inline void Add(int u,int v,int w)
    {
    	e[cnt]=(Line){v,h[u],w};h[u]=cnt++;
    	e[cnt]=(Line){u,h[v],0};h[v]=cnt++;
    }
    char g[MAX][MAX];
    int n,m,bh[MAX][MAX],tot,cur[MAX*MAX];
    bool vis[MAX*MAX],cho[MAX*MAX];
    int d[4][2]={0,1,1,0,-1,0,0,-1};
    int ans,col[MAX*MAX];
    pair<int,int> p[MAX*MAX];
    int level[MAX*MAX],S,T;
    bool bfs()
    {
    	queue<int> Q;memset(level,0,sizeof(level));
    	level[S]=1;Q.push(S);
    	while(!Q.empty())
    	{
    		int u=Q.front();Q.pop();
    		for(int i=h[u];i;i=e[i].next)
    			if(e[i].w&&!level[e[i].v])
    			{
    				level[e[i].v]=level[u]+1,Q.push(e[i].v);
    				if(e[i].v==T)break;
    			}
    	}
    	return level[T];
    }
    int dfs(int u,int flow)
    {
    	if(u==T||!flow)return flow;
    	int ret=0;
    	for(int &i=cur[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(e[i].w&&level[v]==level[u]+1)
    		{
    			int d=dfs(v,min(flow,e[i].w));
    			ret+=d;flow-=d;
    			e[i].w-=d;e[i^1].w+=d;
    			if(!flow)break;
    		}
    	}
    	return ret;
    }
    void DFS(int u,int d)
    {
    	vis[u]=true;
    	if(col[u]==d)++ans,cho[u]=true;
    	for(int i=h[u];i;i=e[i].next)
    		if(e[i].w==d&&!vis[e[i].v])DFS(e[i].v,d);
    }
    void Dinic()
    {
    	int ret=0;
    	while(bfs())
    	{
    		for(int i=S;i<=T;++i)cur[i]=h[i];
    		ret+=dfs(S,1e9);
    	}
    	return;
    }
    int main()
    {
    	n=read();m=read();
    	for(int i=1;i<=n;++i)scanf("%s",g[i]+1);
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			if(g[i][j]!='#')bh[i][j]=++tot;
    	S=0;T=tot+1;
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			if(((i+j)&1)&&g[i][j]=='.')
    				for(int k=0;k<4;++k)
    				{
    					int x=i+d[k][0],y=j+d[k][1];
    					if(x<1||x>n||y<1||y>m||g[x][y]=='#')continue;
    					Add(bh[i][j],bh[x][y],1);
    				}
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			if(g[i][j]=='.')
    			{
    				if((i+j)&1)Add(S,bh[i][j],1),col[bh[i][j]]=1;
    				else Add(bh[i][j],T,1),col[bh[i][j]]=0;
    			}
    	Dinic();DFS(S,1);DFS(T,0);
    	puts(ans?"WIN":"LOSE");sort(&p[1],&p[ans+1]);
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			if(bh[i][j]&&cho[bh[i][j]])printf("%d %d
    ",i,j);
    	return 0;
    }
    
    
    
  • 相关阅读:
    使用 Redis 实现分布式锁(转载)
    使用Redis SETNX 命令实现分布式锁(转载)
    linux目录(转载)
    sleep函数作用(转)
    大数据量高并发的数据库优化(转)
    java获取request的url方法区别
    java获取request的头信息
    打分排序系统漫谈2
    打分排序系统漫谈1
    Tree
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9246209.html
Copyright © 2011-2022 走看看