【BZOJ2437】[Noi2011]兔兔与蛋蛋(博弈+二分图)
题意:
给出一个\(n*m\)的棋盘,上面有若干个黑色棋子,若干个白色棋子,还有一个空格。
每次先手选择一个空格,将它与相邻的某个白色棋子交换;后手则选择一个空格,与相邻的某个黑色棋子交换。
最终不能移动的人输。
现在给出一个棋盘以及游戏过程,最终的结果为先手输掉比赛。请你输出先手在这次比赛中失误了几次以及那几次的序号。
思路:
- 首先明确,先手失误:当且仅当在其操作前存在必胜策略,在其操作后后手存在必胜策略。
- 然后考虑两个人的操作,将其等价于移动空格,先手只能移向白点,后手只能移向黑点。但这么考虑可能出现问题,因为实际过程中,黑点和白点的位置会发生改变,但其实可以证明:不会经过一个格子两次。这样的话直接移动空格的正确性就有保证。
证明的话先考虑较小的情况发现不能形成环,然后行数加一或者列数加一改变了偶数个格子,也不影响。
- 那么问题就转化为:从一个固定的黑点出发,形成一个黑白相间的路径,是否路径中某些点存在必胜策略。
- 如果一个点存在必胜策略的话,那么从它出发肯定只用走奇数步即可,观察到这是一个二分图,也就是说,在一个二分图中,比如从左边出发,最终只能停留在右边;也就是说,左边出发的这个点一定是在最大匹配中的一个点,因为停留在左边意味着有另外一种匹配方案。
- 问题就进一步转化为:给出一个二分图,判定对应的点是否一定在二分图中。那么这个直接将点和边拆掉暴力匈牙利就行。
思路和game这个题有点类似,但又略有一点不同。
注意一下,因为我们不能经过同一个点两次,也就是说如果当前走过了这个点,后面寻找增广路时也不能走这个点。不然思路就前后矛盾了。
细节见代码:
/*
* Author: heyuhhh
* Created Time: 2019/11/7 10:52:33
*/
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 45, M = 2605;
int n, m;
char s[N];
int mp[N][N];
vector <int> g[M];
int id(int x, int y) {
return (x - 1) * m + y;
}
bool ok(int x, int y) {
return x >= 1 && x <= n && y >= 1 && y <= m && mp[x][y] == 0;
}
int T;
int match[M], vis[M], ban[M];
bool win[M];
bool dfs(int x) {
if(ban[x]) return false;
for(int i = 0; i < sz(g[x]); i++){
int it = g[x][i];
if(vis[it] != T && !ban[it]) {
vis[it] = T;
if(match[it] == -1 || dfs(match[it])) {
match[it] = x;
match[x] = it;
return true;
}
}
}
return false;
}
void run(){
cin >> n >> m;
int X, Y;
for(int i = 1; i <= n; i++) {
cin >> (s + 1);
for(int j = 1; j <= m; j++) {
if(s[j] == 'X') mp[i][j] = 1;
else if(s[j] == 'O') mp[i][j] = 0;
else mp[i][j] = 1, X = i, Y = j;
}
}
static int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) if(mp[i][j]) {
for(int k = 0; k < 4; k++) {
int nx = i + dx[k], ny = j + dy[k];
if(ok(nx, ny)) {
g[id(i, j)].push_back(id(nx, ny));
g[id(nx, ny)].push_back(id(i, j));
}
}
}
}
memset(match, -1, sizeof(match));
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) if(mp[i][j]) {
++T; dfs(id(i, j));
}
}
int k; cin >> k;
for(int i = 1; i <= k + k; i++) {
ban[id(X, Y)] = true;
if(match[id(X, Y)] != -1) {
int nw = match[id(X, Y)];
match[id(X, Y)] = match[nw] = -1;
++T;
win[i] = !dfs(nw);
}
cin >> X >> Y;
}
vector <int> res;
for(int i = 1; i <= k; i++) {
if(win[2 * i - 1] && win[2 * i]) res.push_back(i);
}
cout << sz(res) << '\n';
for(int i = 0; i < sz(res); i++) cout << res[i] << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
【BZOJ2437】[Noi2011]兔兔与蛋蛋(博弈+二分图)的更多相关文章
- 【BZOJ2437】【NOI2011】兔兔与蛋蛋(博弈论,二分图匹配)
[BZOJ2437][NOI2011]兔兔与蛋蛋(博弈论,二分图匹配) 题面 BZOJ 题解 考虑一下暴力吧. 对于每个状态,无非就是要考虑它是否是必胜状态 这个直接用\(dfs\)爆搜即可. 这样子 ...
- 【BZOJ 2437】 2437: [Noi2011]兔兔与蛋蛋 (博弈+二分图匹配**)
未经博主同意不得转载 2437: [Noi2011]兔兔与蛋蛋 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 693 Solved: 442 Des ...
- bzoj 2437[Noi2011]兔兔与蛋蛋 黑白染色二分图+博弈+匈牙利新姿势
noi2011 兔兔与蛋蛋 题目大意 直接看原题吧 就是\(n*m\)的格子上有一些白棋和一些黑棋和唯一一个空格 兔兔先手,蛋蛋后手 兔兔要把与空格相邻的其中一个白棋移到空格里 蛋蛋要把与空格相邻的其 ...
- 【bzoj2437】[Noi2011]兔兔与蛋蛋 二分图最大匹配+博弈论
Description Input 输入的第一行包含两个正整数 n.m. 接下来 n行描述初始棋盘.其中第i 行包含 m个字符,每个字符都是大写英文字母"X".大写英文字母&quo ...
- 2437: [Noi2011]兔兔与蛋蛋 - BZOJ
Description Input 输入的第一行包含两个正整数 n.m.接下来 n行描述初始棋盘.其中第i 行包含 m个字符,每个字符都是大写英文字母"X".大写英文字母" ...
- 博弈论(二分图匹配):NOI 2011 兔兔与蛋蛋游戏
Description Input 输入的第一行包含两个正整数 n.m. 接下来 n行描述初始棋盘.其中第i 行包含 m个字符,每个字符都是大写英文字母"X".大写英文字母&quo ...
- NOI2011 兔兔与蛋蛋游戏
http://www.lydsy.com/JudgeOnline/problem.php?id=2437 这道题真是极好的. 75分做法: 搜索. 出题人真的挺良心的,前15个数据点的范围都很小,可以 ...
- 【BZOJ2432】【NOI2011】兔农(数论,矩阵快速幂)
[BZOJ2432][NOI2011]兔农(数论,矩阵快速幂) 题面 BZOJ 题解 这题\(75\)分就是送的,我什么都不想写. 先手玩一下,发现每次每次出现\(mod\ K=1\)的数之后 把它减 ...
- BZOJ2437 NOI2011兔兔与蛋蛋(二分图匹配+博弈)
首先将棋盘黑白染色,不妨令空格处为黑色.那么移动奇数次后空格一定处于白色格子,偶数次后空格一定处于黑色格子.所以若有某个格子的棋子颜色与棋盘颜色不同,这个棋子就是没有用的.并且空格与某棋子交换后,棋子 ...
随机推荐
- Linux之facl----设置文件访问控制列表(详解)
setfacl命令 是用来在命令行里设置ACL(访问控制列表) 选项 -b,--remove-all:删除所有扩展的acl规则,基本的acl规则(所有者,群组,其他)将被保留. -k,--remove ...
- CGAL的安装与使用
CGAL CGAL系大名鼎鼎的计算几何算法库,采用C++语言,代码中大量使用模板,相对比较难读.可以支持float, double, CORE的高精度或者gmp等任意精度库. 安装CGAL 在Wind ...
- React 创建一个自动跟新时间的组件
componentDidMount声明周期函数 表示组件渲染完成后 componentWillUnmount声明周期函数 组件将要卸载 通常用于(为了防止内存泄漏 清除定时器) 11==>创建组 ...
- docker搭建kafka环境&&Golang生产和消费
docker 搭建kafka环境 version: '2' services: zk1: image: confluentinc/cp-zookeeper:latest hostname: zk1 c ...
- echarts 中 参数的详讲
xAxis 属性 xAxis : [ { type : 'category',//坐标轴类型 // show:'',//是否显示 x 轴 //id:'',组件 ID.默认不指定. //gridInde ...
- 【STM32H7教程】第17章 STM32H7之GPIO的HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第17章 STM32H7之GPIO的HAL库API ...
- css top,right,bottom,left设置为0有什么用?它和width:100%和height:100%有什么区别?
壹 ❀ 引 当我们使用position属性时,总免不了与top,left,right,bottom四个属性打交道,那么这四个属性都设置为0时有什么用,与宽高设置100%又有什么区别?本文对此展开讨论 ...
- Shell脚本中的while getopts用法小结
getpots是Shell命令行参数解析工具,旨在从Shell Script的命令行当中解析参数.getopts被Shell程序用来分析位置参数,option包含需要被识别的选项字符,如果这里的字符后 ...
- 重磅来袭!Reactive 架构专场四城巡回演讲
Reactive 究竟是什么?Reactive 对架构设计的影响和冲击,以及给开发方式带来的改变有哪些?为什么阿里巴巴.Pivotal.Facebook 纷纷在生产环境中实践 Reactive? 本次 ...
- PHP作用域和文件夹操作
1.作用域 1.1变量作用域 1.全局变量:在函数外面 2.局部变量:在函数里面,默认情况下,函数内部是不会访问函数外部的变量 3.超全局变量:可以在函数内 ...