【BZOJ1443】【JSOI2009】游戏Game 二分图+博弈

#include <stdio.h>
int main()
{
	puts("转载请注明出处谢谢");
	puts("http://blog.csdn.net/vmurder/article/details/43311795");
}

题解:二分图博弈经典模型模板题。

首先黑白染色。

然后我们考虑到有一种优秀的走法,

就是先求个最大匹配,然后如果先手选择了一个最大匹配中的点,那么显然后手可以依照此点的匹配再走一步,然后先手无法走此匹配,就乱走一气,于是有两种情况:

一、又选了一个最大匹配中的点,显然回到了情况一。

二、他选了一个不在最大匹配中的点(显然是有这种情况的),如下图,先手走3,后手走2,先手走1赢了。

于是,诶?先手赢了,靠啊,我考虑错了。

那么换一种方式考虑。

就是先手先选一个不在最大匹配里面的点,然后对手有两种情况:

一、走一个在最大匹配里的点,然后有了上面考虑错的那种情况,但是不同的是,如果出现了后手最后走某边达到一个非最大匹配中点,就代表出现了一条增广路,显然因为是最大匹配,所以这种情况是不会出现的,所以这种情况先手必胜。

二、走一个不在最大匹配里的点,然后?诶?这是显然的不对啊!直接增广了,连反向弧神马都不用!!!

于是~~~先手必胜。

但是如果所有的点都最大匹配了呢?(我整个人都最大匹配了)

随便先手去选哪个点,后手都可以沿其匹配走,然后先手如果走,又是一个新的最大匹配中点,后手又走其匹配,然后后手就赢了。

胜负就是这种情况了~~~

然后是win时方案:

我们考虑到首先先手一定可以走一个非匹配点,显然这就赢了。

但是如果只输出这些点,显然样例都过不了!!!

我们拿上面那个图举例,就可以有两种选择。

这个怎么做呢?

我是拿网络流写的,这里就先介绍一种最大流时的做法吧:

我们dfs一遍,看从源点能到哪些染色时归到S集的点;

再dfs一遍,看从哪些染色时归到T集的点能到汇点。

这些点就是答案!(注意建边时需要为有向边!就是反向弧初始流量为0!)

为什么呢?

首先非匹配点肯定是会被扫到的,

然后是匹配点中的可行点:

我们将如何扫到这些可行点呢?

以S集点为例,我们会先扫到一个可行点,然后扫到一个T集点,然后再通过已经有流量的反向弧回到一个匹配点,这样这个匹配点就是可行的了!

原因:这显然是一个类似于增广路的过程,而最后的结果是我们用这个可行点代替了扫到的匹配点,并与那个T集点构成了匹配。、呃,不是很难理解吧?

代码:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define P 200
#define N 101000
#define M 501000
#define inf 0x3f3f3f3f
using namespace std;
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
struct KSD
{
	int v,len,next;
}e[M];
int head[N],cnt;
inline void add(int u,int v,int len)
{
	e[++cnt].v=v;
	e[cnt].len=len;
	e[cnt].next=head[u];
	head[u]=cnt;
}
inline void Add(int u,int v,int len){add(u,v,len),add(v,u,0);}
int s,t,d[N];
queue<int>q;
bool bfs()
{
	while(!q.empty())q.pop();
	memset(d,0,sizeof d);

	int i,u,v;
	q.push(s),d[s]=1;
	while(!q.empty())
	{
		u=q.front(),q.pop();
		for(i=head[u];i;i=e[i].next)
		{
			if(!d[v=e[i].v]&&e[i].len)
			{
				d[v]=d[u]+1;
				if(v==t)return 1;
				q.push(v);
			}
		}
	}
	return 0;
}
int dinic(int x,int flow)
{
	if(x==t)return flow;
	int remain=flow,i,v,k;
	for(i=head[x];i;i=e[i].next)
	{
		if(d[v=e[i].v]==d[x]+1&&e[i].len)
		{
			k=dinic(v,min(remain,e[i].len));
			if(!k)d[v]=0;
			e[i].len-=k,e[i^1].len+=k;
			remain-=k;
		}
	}
	return flow-remain;
}
char map[P][P];
int maxflow,n,m;
int id[P][P],idx[N],idy[N];
bool belong[N];
void build()
{
	int i,j,k;
	int x,y;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
	{
		scanf("%s",map[i]+1);
		for(j=1;j<=m;j++)if(map[i][j]=='.')
		{
			id[i][j]=++cnt;
			idx[cnt]=i;
			idy[cnt]=j;
		}
	}
	s=cnt+1,t=cnt+2,cnt=1;
	for(i=1;i<=n;i++)for(j=1;j<=m;j++)if(id[i][j])
	{
		maxflow++;
		if(i+j&1)
		{
			Add(s,id[i][j],1);
			belong[id[i][j]]=true;
			for(k=0;k<4;k++)
			{
				x=i+dx[k],y=j+dy[k];
				if(id[x][y])Add(id[i][j],id[x][y],1);
			}
		}
		else Add(id[i][j],t,1);
	}
}
bool vis[N];
int ans[N];
void dfs(int x,int d)
{
	int i,v;
	vis[x]=true;
	if(belong[x]==d)ans[++cnt]=x;
	for(i=head[x];i;i=e[i].next)
		if(e[i].len==d&&!vis[v=e[i].v])
			dfs(v,d);
}
int main()
{
	freopen("test.in","r",stdin);
	build();
	while(bfs())maxflow-=2*dinic(s,inf);
	if(!maxflow)
	{
		puts("LOSE");
		return 0;
	}
	else {
		puts("WIN");
		cnt=0;
		dfs(s,1);
		memset(vis,0,sizeof vis);
		dfs(t,0);
		sort(ans+1,ans+cnt+1);
		for(int i=1;i<=cnt;i++)if(ans[i]!=s&&ans[i]!=t)
			printf("%d %d\n",idx[ans[i]],idy[ans[i]]);
		return 0;
	}
	return 0;
}
时间: 2024-12-25 18:36:47

【BZOJ1443】【JSOI2009】游戏Game 二分图+博弈的相关文章

BZOJ1443: [JSOI2009]游戏Game

如果没有不能走的格子的话,和BZOJ2463一样,直接判断是否能二分图匹配 现在有了一些不能走的格子 黑白染色后求出最大匹配 如果是完备匹配,则无论如何后手都能转移到1*2的另一端,故先手必输 否则的话,将棋子放在不是必须点的点上则先手必赢 证明是这样的: 先手先选一个不在最大匹配里面的点,然后对手有两种情况: 一.走一个在最大匹配里的点,然后有了上面考虑错的那种情况,但是不同的是,如果出现了后手最后走某边达到一个非最大匹配中点,就代表出现了一条增广路,显然因为是最大匹配,所以这种情况是不会出现

BZOJ 1443 JSOI 2009 游戏Game 二分图+博弈

题目大意:给出一个带有坏点的网格图,每次移动棋子到相邻的格子中,要求格子不能重复,问先手是否有必胜策略,如果有,输出所有的棋子可以摆放的初值位置. 思路:很经典的二分图博弈模型,将图黑白染色,就变成了二分图.求最大匹配之后,如果是在二分匹配上的边,每次先手从左侧走到右侧,后手就一定能从右边走回来,这样就是先手输了.具体见:http://blog.sina.com.cn/s/blog_76f6777d0101inwe.html 建立网络流模型,跑最大流,在残量网络上从S能够搜到的左侧的点和右侧的能

JSOI2009 游戏

1443: [JSOI2009]游戏Game Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 557  Solved: 251[Submit][Status] Description Input 输入数据首先输入两个整数N,M,表示了迷宫的边长. 接下来N行,每行M个字符,描述了迷宫. Output 若小AA能够赢得游戏,则输出一行"WIN",然后输出所有可以赢得游戏的起始位置,按行优先顺序输出 每行一个,否则输出一行"LOSE

HDU 1527 取石子游戏 威佐夫博弈

题目来源:HDU 1527 取石子游戏 题意:中文 思路:威佐夫博弈 必败态为 (a,b ) ai + i = bi     ai = i*(1+sqrt(5.0)+1)/2   这题就求出i然后带人i和i+1判断是否成立 以下转自网上某总结 有公式ak =[k(1+√5)/2],bk= ak + k  (k=0,1,2,-,n 方括号表示取整函数) 其中出现了黄金分割数(1+√5)/2 = 1.618-,因此,由ak,bk组成的矩形近似为黄金矩形 由于2/(1+√5)=(√5-1)/2,可以先

HDU 2177 取(2堆)石子游戏 威佐夫博弈

题目来源:HDU 2177 取(2堆)石子游戏 题意:中文 思路:判断是否是必败态就不说了 做过hdu1527就知道了 现在如果不是必败态 输出下一步所有的必败态 题目要求先输出两堆都取的方案 首先 a = b 直接2堆取完 a != b 因为bi = ai+i 现在知道ak 和 bk 那么 k = bk-ak 得到k 求出 aj 和 bj 如果ak-aj == bk-bj && ak-aj > 0(aj, bj)是必败态 输出aj bj 然后是只取一堆的情况 假设a不变 求出对应的

【BZOJ4554】游戏(二分图匹配,网络流)

[BZOJ4554]游戏(二分图匹配,网络流) 题解 Description 在2016年,佳缘姐姐喜欢上了一款游戏,叫做泡泡堂.简单的说,这个游戏就是在一张地图上放上若干个炸弹,看 是否能炸到对手,或者躲开对手的炸弹.在玩游戏的过程中,小H想到了这样一个问题:当给定一张地图,在这张 地图上最多能放上多少个炸弹能使得任意两个炸弹之间不会互相炸到.炸弹能炸到的范围是该炸弹所在的一行和一 列,炸弹的威力可以穿透软石头,但是不能穿透硬石头.给定一张nm的网格地图:其中代表空地,炸弹的威力可 以穿透,可

BZOJ:1443: [JSOI2009]游戏Game

原题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1443 反正不看题解我是完全想不出系列-- 先把棋盘黑白染色,也就是同一对角线上颜色相同,使得一个格子上下左右都不同色. 然后我们会发现,某一个人所走的全部格子颜色都是相同的. 把黑白格子当作点提取出来,放在两边,就变成了二分图,游戏的全过程变得像匈牙利算法的增广. 这提示我们也许跟二分图匹配有关. 如果一个点必定在最大匹配中,而一开始棋子放在了这里小YY只要沿着匹配边走小AA就gg了.

【BZOJ1115】[POI2009]石子游戏Kam 阶梯博弈

[BZOJ1115][POI2009]石子游戏Kam Description 有N堆石子,除了第一堆外,每堆石子个数都不少于前一堆的石子个数.两人轮流操作每次操作可以从一堆石子中移走任意多石子,但是要保证操作后仍然满足初始时的条件谁没有石子可移时输掉游戏.问先手是否必胜. Input 第一行u表示数据组数.对于每组数据,第一行N表示石子堆数,第二行N个数ai表示第i堆石子的个数(a1<=a2<=……<=an). 1<=u<=10 1<=n<=1000 0<=

洛谷P2252 取石子游戏(威佐夫博弈)

题目背景 无 题目描述 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两堆中同时取走相同数量的石子.最后把石子全部取完者为胜者.现在给出初始的两堆石子的数目,你先取,假设双方都采取最好的策略,问最后你是胜者还是败者. 输入输出格式 输入格式: 输入共一行. 第一行共两个数a, b,表示石子的初始情况. 输出格式: 输出共一行. 第一行为一个数字1.0或-1,如果最后你是胜利者则为1:若失败则为0:若结