【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\n",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\n",i,j);
return 0;
}

【BZOJ1443】游戏(二分图匹配,博弈论)的更多相关文章

  1. BZOJ-1854 游戏 二分图匹配 (并查集)

    1854: [Scoi2010]游戏 Time Limit: 5 Sec Memory Limit: 162 MB Submit: 3372 Solved: 1244 [Submit][Status] ...

  2. bzoj4554: [Tjoi2016&Heoi2016]游戏 二分图匹配

    4554: [Tjoi2016&Heoi2016]游戏 Description 在2016年,佳缘姐姐喜欢上了一款游戏,叫做泡泡堂.简单的说,这个游戏就是在一张地图上放上若干个炸弹,看 是否能 ...

  3. BZOJ 4554: [Tjoi2016&Heoi2016]游戏 二分图匹配

    4554: [Tjoi2016&Heoi2016]游戏 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=4554 Descripti ...

  4. BZOJ 1059 矩阵游戏 二分图匹配

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1059 题目大意: 小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏 ...

  5. BZOJ1854: [Scoi2010]游戏(二分图匹配)

    题目描述 lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示.当他使用某种装备时,他只能使用该装备的某一个属性.并且每种装备 ...

  6. BZOJ [ZJOI2007]矩阵游戏(二分图匹配)

    1059: [ZJOI2007]矩阵游戏 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 6390  Solved: 3133[Submit][Stat ...

  7. HDU1281-棋盘游戏-二分图匹配

    先跑一个二分图匹配,然后一一删去匹配上的边,看能不能达到最大匹配数,不能这条边就是重要边 /*----------------------------------------------------- ...

  8. BZOJ1059 [ZJOI2007]矩阵游戏 二分图匹配 匈牙利算法

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1059 题意概括 有一个n*n(n<=200)的01矩阵,问你是否可以通过交换整行和整列使得左 ...

  9. 1059: [ZJOI2007]矩阵游戏 二分图匹配

    https://www.lydsy.com/JudgeOnline/problem.php?id=1059 裸的二分图匹配,行列匹配即可 /****************************** ...

  10. BZOJ 1854 游戏(二分图匹配或并查集)

    此题的二分图匹配做法很容易想,就是把属性当做s集,武器当做t集,如果该武器拥有该武器则连一条边. 那么答案就是求该二分图的最大前i个匹配.将匈牙利算法改一改,当前找不到增广路就break. 但是过这个 ...

随机推荐

  1. idea css文件不识别的问题的解决方案

    可以看到 progressBar不识别 发现仅仅是这一个文件名不识别 所以换一个任意别的文件名即可识别

  2. Entity Framework中的几种加载方式

            在Entity Framework中有三种加载的方式,分别是延迟加载,自动加载和显示加载.下面用一个例子来说明:现在有两个表,一个是资料表(Reference),另外一个表是资料分类表 ...

  3. 内容安全策略(CSP)

    内容安全策略(CSP),其核心思想十分简单:网站通过发送一个 CSP 头部,来告诉浏览器什么是被授权执行的与什么是需要被禁止的.其被誉为专门为解决XSS攻击而生的神器. 1.CSP是什么 CSP指的是 ...

  4. 【Jmeter测试】BeanShell介绍和使用

      BeanShell是什么? BeanShell是一种完全符合Java语法规范的脚本语言,并且又拥有自己的一些语法和方法; BeanShell是一种松散类型的脚本语言: BeanShell是用Jav ...

  5. 关于scrum敏捷测试

    关于scrum的一些定义 敏捷软件开发方法是一种把新增功能通过较小的循环逐步迭代添加到项目中(的项目管理方法),工作是由自我组织的团队以高效合作的方式拥抱和适应变化来保证客户需求被真正满足的方式来完成 ...

  6. Linux 安装Zookeeper<集群版>(使用Mac远程访问)

    阅读本文需要先阅读安装Zookeeper<准备> 一 架构细节 zookeeper集群根据投票选举的机制 选出leader和follower zookeeper集群节点建议是奇数 这里我准 ...

  7. List Leaves 树的层序遍历

    3-树2 List Leaves (25 分) Given a tree, you are supposed to list all the leaves in the order of top do ...

  8. 学习使用Git 版本控制 代码管理

    title: 学习使用Git 版本控制 代码管理 notebook: 经验累积 tags:Git --- Git 版本控制 学习教程 Git版本控制器,可以作为程序员.计算机科学和软件工程的研究人员在 ...

  9. YQCB项目介绍

    YQCB记账本软件 制作人:YQCB团队 团队简介:团队成立于2017年11月21日,由陈美琪,张晨阳,邢全阳,刘昭为四人组成. 陈美琪:团队灵魂人物,背负着巨大的压力带起整个团队. 张晨阳:团队领军 ...

  10. 第8章 Linux磁盘与文件系统管理

    认识EXT2文件系统 文件的系统特性 Linux的正规文件系统为Ext2 文件数据除了文件实际内容外,还包括其他属性(文件权限.文件属性). 文件系统将这两部分数据分别存放在不同的块,权限和属性放在i ...