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

题目大意:给出一个带有坏点的网格图,每次移动棋子到相邻的格子中,要求格子不能重复,问先手是否有必胜策略,如果有,输出所有的棋子可以摆放的初值位置。

思路:很经典的二分图博弈模型,将图黑白染色,就变成了二分图。求最大匹配之后,如果是在二分匹配上的边,每次先手从左侧走到右侧,后手就一定能从右边走回来,这样就是先手输了。具体见:http://blog.sina.com.cn/s/blog_76f6777d0101inwe.html

建立网络流模型,跑最大流,在残量网络上从S能够搜到的左侧的点和右侧的能够搜到T的点都是满足要求的点。

CODE:

#define _CRT_SECURE_NO_WARNINGS

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 2100
#define MAXP 20100
#define MAXE 1000010
#define S 0
#define T (MAXP - 1)
#define INF 0x3f3f3f3f
using namespace std;
const int dx[] = {0,1,-1,0,0};
const int dy[] = {0,0,0,1,-1};

int ans[MAXP];
bool v[MAXP];

struct MaxFlow{
	int head[MAXP],total;
	int next[MAXE],aim[MAXE],flow[MAXE];

	int deep[MAXP];

	MaxFlow():total(1) {}
	void Add(int x,int y,int f) {
		next[++total] = head[x];
		aim[total] = y;
		flow[total] = f;
		head[x] = total;
	}
	void Insert(int x,int y,int f) {
		//cout << x << ' ' << y << ' ' << f << endl;
		Add(x,y,f);
		Add(y,x,0);
	}
	bool BFS() {
		static queue<int> q;
		while(!q.empty())	q.pop();
		memset(deep,0,sizeof(deep));
		deep[S] = 1;
		q.push(S);
		while(!q.empty()) {
			int x = q.front(); q.pop();
			for(int i = head[x]; i; i = next[i])
				if(flow[i] && !deep[aim[i]]) {
					deep[aim[i]] = deep[x] + 1;
					q.push(aim[i]);
					if(aim[i] == T)	return true;
				}
		}
		return false;
	}
	int Dinic(int x,int f) {
		if(x == T)	return f;
		int temp = f;
		for(int i = head[x]; i; i = next[i])
			if(flow[i] && deep[aim[i]] == deep[x] + 1 && temp) {
				int away = Dinic(aim[i],min(flow[i],temp));
				if(!away)	deep[aim[i]] = 0;
				flow[i] -= away;
				flow[i^1] += away;
				temp -= away;
			}
		return f - temp;
	}
}solver;

int m,n;
char s[MAX][MAX];
int num[MAX][MAX];
bool col[MAXP];

bool t[MAXP];

void DFS(int x,int f)
{
	t[x] = true;
	if(col[x] == f && x != S && x != T)
		ans[++ans[0]] = x;
	for(int i = solver.head[x]; i; i = solver.next[i])
		if(solver.flow[i] == f && !t[solver.aim[i]])
			DFS(solver.aim[i],f);
}

void GetAns()
{
	DFS(S,1);
	memset(t,false,sizeof(t));
	DFS(T,0);
}

inline void OutPut(int x)
{
	printf("%d %d\n",(x - 1) / n + 1,(x - 1) % n + 1);
}

int main()
{
	cin >> m >> n;
	for(int i = 1; i <= m; ++i)
		scanf("%s",s[i] + 1);
	int cnt = 0;
	for(int i = 1; i <= m; ++i)
		for(int j = 1; j <= n; ++j)
			num[i][j] = ++cnt;
	for(int i = 1; i <= m; ++i)
		for(int j = 1; j <= n; ++j) {
			if(s[i][j] == '#')	continue;
			if(!((i + j)&1))
				solver.Insert(S,num[i][j],1),col[num[i][j]] = true;
			else {
				solver.Insert(num[i][j],T,1);
				continue;
			}
			for(int k = 1; k <= 4; ++k) {
				int fx = i + dx[k],fy = j + dy[k];
				if(!fx || !fy || fx > m || fy > n)	continue;
				if(s[fx][fy] == '.')
					solver.Insert(num[i][j],num[fx][fy],1);
			}
		}
	while(solver.BFS())
		solver.Dinic(S,INF);
	GetAns();
	if(!ans[0])	puts("LOSE");
	else {
		puts("WIN");
		sort(ans + 1,ans + ans[0] + 1);
		for(int i = 1; i <= ans[0]; ++i)
			OutPut(ans[i]);
	}
	return 0;
}

时间: 2025-01-06 03:01:14

BZOJ 1443 JSOI 2009 游戏Game 二分图+博弈的相关文章

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

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43311795"); } 题解:二分图博弈经典模型模板题. 首先黑白染色. 然后我们考虑到有一种优秀的走法, 就是先求个最大匹配,然后如果先手选择了一个最大匹配中的点,那么显然后手可以依照此点的匹配再走一步,然后先手无法走此匹配,就乱走一气,于是有两种

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

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1059 题意: 给你一个n*n的01矩阵. 你可以任意次地交换某两行或某两列. 问你是否可以让这个矩阵的主对角线(左上角到右下角的连线)上的格子均为黑色. 题解: 可以发现,对于一个格子,无论怎样移动,它原来行(列)上的格子还是在现在的行(列)上. 因为最终目标是将n个黑色格子移到对角线上,所以只要有n个黑色格子的行列均不相同即可. 那么对于每个黑色格子,将行号i向列号j连一条边,然后跑二

BZOJ 1452 JSOI 2009 Count 二维树状数组

题目大意:有一个m*n的方格,每一个格子有他自己的权值.2种操作: 1.改变一个格子的权值. 2.查询所有的x1 <= x <= x2 && y1 <= y <= y2的中,有多少个格子颜色是c. 思路:好像是二维树状数组的样子,但是不知道怎么搞.后来研究了数据范围,发现格子最大300*300,颜色最多才100种,于是算一下300*300*100*4/1024/1024大概是35M,题目要求64M,可以搞了.(这里算的精确一点,我当时没怎么算,吧颜色开成300的了,

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

1059: [ZJOI2007]矩阵游戏 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1891  Solved: 919[Submit][Status] Description 小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏——矩阵游戏.矩阵游戏在一个N*N黑白方阵进行(如同国际象棋一般,只是颜色是随意的).每次可以对该矩阵进行两种操作:行交换操作:选择矩阵的任意两行,交换这两行(即交换对应格子的颜色)列交换操作:选择矩阵

[Wikioi 2913][BZOJ 1029][JSOI 2007]建筑抢修

题目描述 Description 小刚在玩JSOI提供的一个称之为"建筑抢修"的电脑游戏: 经过了一场激烈的战斗,T部落消灭了所有z部落的入侵者.但是T部落的基地里已经有N个建筑设施受到了严重的损伤,如果不尽快修复的话,这些建筑设施将会完全毁坏.现在的情况是:T部落基地里只有一个修理工人,虽然他能瞬间到达任何一个建筑,但是修复每个建筑都需要一定的时间.同时,修理工人修理完一个建筑才能修理下一个建筑,不能同时修理多个建筑.如果某个建筑在一段时间之内没有完全修理完毕,这个建筑就报废了.你的

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,可以先

BZOJ 2756 奇怪的游戏(最大流)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2756 题意:在一个 N*M 的棋盘上玩,每个格子有一个数.每次 选择两个相邻的格子,并使这两个数都加上 1. 问最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同一个数则输出-1. 思路:对棋盘进行黑白染色,则每次操作使得黑白两色的格子总和各增加1.设黑色总和s1,个数cnt1:白色总和s2,个数cnt2,设最后的数字为x,那么有: x*cnt1-s1=x*cnt2-s2. (

BZOJ 1413 取石子游戏(DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1413 题意:n堆石子排成一排.每次只能在两侧的两堆中选择一堆拿.至少拿一个.谁不能操作谁输. 思路:参考这里. int f1[N][N],f2[N][N],n,a[N]; void deal() { RD(n); int i,j,k; FOR1(i,n) RD(a[i]),f1[i][i]=f2[i][i]=a[i]; int p,q,x; for(k=2;k<=n;k++) for(

BZOJ 1978 取数游戏(DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1978 题意:给出一个数列a,在其中找出下标依次增大的数,使得任意相邻的两个数的最大公约数大于等于m.找出最多的数字. 思路:f[i]表示前面的数字中最大公约数为i可以找出的最多的数字个数.那么对于当前数字x: 接着更新f: int f[N],a[N]; int n,m; int main() { RD(n,m); int i; FOR1(i,n) RD(a[i]); int j,k;