BZOJ.1443.[JSOI2009]游戏Game(二分图博弈 匈牙利)
\(Description\)
一个\(N*M\)的有障碍的棋盘,先手放置棋子后,从后手开始轮流移动棋子,不能走重复的位置,不能移动的输。求在哪些位置放棋子是先手必胜的。
\(Solution\)
依旧先黑白染色,移动棋子对应一个匹配。
那么原图有两种情况:
一是存在完美匹配:那么无论先手选哪个点开始,假设是S集合某点,那么后手沿匹配边走,先手要么沿匹配边再走到S集合某点,要么没法走。即先手必败;
二是不存在完美匹配:
1.先手从最大匹配点开始,好像胜负情况都有,先不考虑;
2.先手从非最大匹配点开始,后手只能走到一个最大匹配点(若能走到非匹配点则又是一个匹配,与最大匹配矛盾),然后先手再走匹配边,发现后手只能走匹配边。
因为当前点如果存在非匹配边,则与起点那个非匹配点又形成了一条增广路,与最大匹配矛盾。
那这又成了情况一了,即后手必败。
即如果起点是非最大匹配点则必胜。起点只要在某种最大匹配下不是最大匹配点就满足。
再看情况二的1,如果起点可以不是最大匹配点,则先手必胜。否则先手必败,和情况二的2的结论一样。
现在问题是判断有哪些点不一定在最大匹配中。首先跑一遍最大匹配,未匹配的点肯定是。
然后这些未匹配点\(x\)可以替换掉邻接点\(v\)的一条匹配边,即\(match[v]\)也可以不在最大匹配中(原先的匹配边\(v\rightarrow match[v]\)替换为\(v\rightarrow x\))。
对未匹配点DFS一遍就可以了。
复杂度在于匹配,\(O(n^2)\)?
刚想起来最大匹配要拆点。。或者黑白染色?也不用拆点或者染色,每个点向四周都连边即可。
不拆点要注意match[]/lk[]这个数组对两边的集合都要给它赋值,匹配(bool OK())的时候保证它之前没有匹配。。
做题前已经忘了最大匹配长什么样了...
//1680kb 256ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 350000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define ID(i,j) ((i-1)*m+j)
#define Ck(i,j) (mp[i][j]&&1<=(i)&&(i)<=n&&1<=(j)&&(j)<=m)
const int N=10005,M=N<<2;
int n,m,vis[N],Time,Enum,H[N],nxt[M],to[M],lk[N],q[N];
bool mp[105][105],ok[N];
char IN[MAXIN],*SS=IN,*TT=IN;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
#define AE(u,v) to[++Enum]=v,nxt[Enum]=H[u],H[u]=Enum,to[++Enum]=u,nxt[Enum]=H[v],H[v]=Enum
bool OK(int x)
{
vis[x]=Time;
for(int i=H[x],v; i; i=nxt[i])
if(vis[v=to[i]]!=Time)
{
vis[v]=Time;
if(!lk[v]||OK(lk[v])) return lk[v]=x,lk[x]=v;//两个都有啊
}
return 0;
}
void DFS(int x)
{
ok[x]=1;
for(int i=H[x]; i; i=nxt[i])
if(lk[to[i]] && !ok[lk[to[i]]]/*vis[lk[to[i]]]!=Time*/) DFS(lk[to[i]]);
}
int main()
{
n=read(), m=read();
for(int i=1; i<=n; ++i)
{
register char c=gc();
for(; c!='.'&&c!='#'; c=gc());
for(int j=1; j<=m; ++j,c=gc()) mp[i][j]=c=='.';
}
int t=0;
for(int i=1,tot=0; i<=n; ++i)
for(int j=1; j<=m; ++j)
if(++tot,mp[i][j])
{
if(mp[i+1][j]/*i+1<=n*/) AE(tot,tot+m);
if(mp[i][j+1]) AE(tot,tot+1);
}
for(int i=1,tot=0; i<=n; ++i)
for(int j=1; j<=m; ++j)
if((++tot,mp[i][j]) && !lk[tot]/*!*/ && (++Time,!OK(tot)))
q[++t]=tot;
if(!t) return puts("LOSE"),0;
puts("WIN");
for(int i=1; i<=t; ++i) DFS(q[i]);
for(int i=1,tot=0; i<=n; ++i)
for(int j=1; j<=m; ++j)
if(ok[++tot]) printf("%d %d\n",i,j);
return 0;
}
BZOJ.1443.[JSOI2009]游戏Game(二分图博弈 匈牙利)的更多相关文章
- BZOJ:1443: [JSOI2009]游戏Game
原题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1443 反正不看题解我是完全想不出系列…… 先把棋盘黑白染色,也就是同一对角线上颜色相同,使 ...
- BZOJ 1443 [JSOI2009]游戏Game ——博弈论
好题. 首先看到棋盘,先黑白染色. 然后就是二分图的经典模型. 考虑最特殊的情况,完美匹配,那么先手必胜, 因为无论如何,先手走匹配边,后手无论走哪条边,总有对应的匹配边. 如果在不在最大匹配中出发, ...
- 【BZOJ】1443: [JSOI2009]游戏Game
[算法]博弈论+二分图匹配(最大流) [题解]方格图黑白染色得到二分图, 二分图博弈:当起点不属于某个最大匹配时,后手必胜. 问题转化为那些点不属于某个最大匹配. 先找到一个最大匹配,非匹配点加入答案 ...
- BZOJ.2437.[NOI2011]兔兔与蛋蛋游戏(二分图博弈 匈牙利)
题目链接 首先空格的移动等价于棋子在黑白格交替移动(设起点移向白格就是黑色),且不会走到到起点距离为奇数的黑格.到起点距离为偶数的白格(删掉就行了),且不会重复走一个格子. (然后策略就同上题了,只不 ...
- BZOJ:[JSOI2009]游戏Game【二分图匹配乱搞】
题目大意:n*m的棋盘,其中有些区域是禁区,两个人在棋盘上进行博弈,后手选择棋子的初始位置,然后先后手轮流将棋子往上下左右移动,走过的区域不能再走,问能否有一个位置使得后手必胜 Input 输入数据首 ...
- bzoj 2437[Noi2011]兔兔与蛋蛋 黑白染色二分图+博弈+匈牙利新姿势
noi2011 兔兔与蛋蛋 题目大意 直接看原题吧 就是\(n*m\)的格子上有一些白棋和一些黑棋和唯一一个空格 兔兔先手,蛋蛋后手 兔兔要把与空格相邻的其中一个白棋移到空格里 蛋蛋要把与空格相邻的其 ...
- BZOJ 1854: [Scoi2010]游戏(二分图匹配/并查集)
题面: https://www.lydsy.com/JudgeOnline/problem.php?id=1854 题解: 1.二分图匹配: 首先我们发现每件装备只能在两种属性中选一种.因此,我们以每 ...
- 【BZOJ1443】游戏(二分图匹配,博弈论)
[BZOJ1443]游戏(二分图匹配,博弈论) 题面 BZOJ 题解 很明显的二分图博弈问题. 发现每次移动一定是从一个黑点到达一个白点,或者反过来. 所以可以对于棋盘进行染色然后连边. 考虑一下必胜 ...
- BZOJ1443: [JSOI2009]游戏Game
如果没有不能走的格子的话,和BZOJ2463一样,直接判断是否能二分图匹配 现在有了一些不能走的格子 黑白染色后求出最大匹配 如果是完备匹配,则无论如何后手都能转移到1*2的另一端,故先手必输 否则的 ...
随机推荐
- BackBone结合ASP.NET MVC实现页面路由操作
1. 问题的背景 什么是页面路由操作,就是通过浏览器地址栏的标记来实现页面内部的一些操作,这些操作具有异步性和持久性.应用场景主要有页面操作过程中的添加收藏夹的操作.后退操作等过程中能完全恢复界面. ...
- C++学习8-面向对象编程基础(模板)
模板 模板是一种工具,模板可以使程序员能建立具有通用类型的函数库与类库: 模板具有两种不同的形式: 函数模板 类模板 函数模板 当一个add()函数接收两个参数,因为某种特定情况,所传入的实参数据类型 ...
- [转]AMBA、AHB、APB、ASB总线简介
[转]http://www.cnblogs.com/zhaozhong1989/articles/3092140.html 1.前言 随着深亚微米工艺技术日益成熟,集成电路芯片的规模越来越大.数字IC ...
- C#实现Zip压缩解压实例
原文地址:https://www.cnblogs.com/GoCircle/p/6544678.html 本文只列举一个压缩帮助类,使用的是有要添加一个dll引用ICSharpCode.SharpZi ...
- java 基础(转自索宁)
一.方法函数 函数也称为方法,就是定义在类中的具有特定功能的一段独立代码.用于定义功能,提高代码的复用性. 函数的特点1> 定义函数可以将功能代码进行封装,便于对该功能进行复用:2> 函数 ...
- 队列queue 代码
import queue q=queue.Queue(3) #队列:先进先出 q.put(1) q.put(2) q.put(3) q.put(4) print(q.get()) print(q.ge ...
- PYTHON-函数的定义与调用,返回值,和参数-练习
# day10函数的定义调用和参数作业# 1.写函数,用户传入修改的文件名.与要修改的内容,执行函数,完成批量修改操作# def modify_file(filename,old,new):# imp ...
- bert中的分词
直接把自己的工作文档导入的,由于是在外企工作,所以都是英文写的 chinese and english tokens result input: "我爱中国",tokens:[&q ...
- window 命令大全
运行操作 CMD命令:开始->运行->键入cmd或command(在命令行里可以看到系统版本.文件系统版本) CMD命令锦集 1. gpedit.msc-----组策略 2. ...
- 【linux】ubuntu下crontab无效解决方法
在Debain的docker中启用crontab,踩了一整天的坑,特地记录一下.Debain和ubuntu差不多,故算在ubuntu下面了. 1.第一个坑,安装crontab apt-get inst ...