洛谷P1971 [NOI2011] 兔兔与蛋蛋游戏

题目描述

这些天,兔兔和蛋蛋喜欢上了一种新的棋类游戏。

这个游戏是在一个 𝑛n 行 𝑚m 列的棋盘上进行的。游戏开始之前,棋盘上有一个格子是空的,其它的格子中都放置了一枚棋子,棋子或者是黑色,或者是白色。

每一局游戏总是兔兔先操作,之后双方轮流操作,具体操作为:

  • 兔兔每次操作时,选择一枚与空格相邻的白色棋子,将它移进空格。
  • 蛋蛋每次操作时,选择一枚与空格相邻的黑色棋子,将它移进空格。

第一个不能按照规则操作的人输掉游戏。为了描述方便,下面将操作“将第x行第y列中的棋子移进空格中”记为 𝑀(𝑥,𝑦)M(x,y)。

例如下面是三个游戏的例子。

最近兔兔总是输掉游戏,而且蛋蛋格外嚣张,于是兔兔想请她的好朋友——你——来帮助她。她带来了一局输给蛋蛋的游戏的实录,请你指出这一局游戏中所有她“犯错误”的地方。

注意:

  • 两个格子相邻当且仅当它们有一条公共边。
  • 兔兔的操作是“犯错误”的,当且仅当,在这次操作前兔兔有必胜策略,而这次操作后蛋蛋有必胜策略。

输入格式

输入的第一行包含两个正整数 𝑛,𝑚n,m。

接下来 𝑛n 行描述初始棋盘。其中第 𝑖i 行包含 𝑚m 个字符,每个字符都是大写英文字母 X、大写英文字母 O 或点号 . 之一,分别表示对应的棋盘格中有黑色棋子、有白色棋子和没有棋子。其中点号 . 恰好出现一次。

接下来一行包含一个整数 𝑘k(1≤𝑘≤10001≤k≤1000) ,表示兔兔和蛋蛋各进行了 𝑘k 次操作。

接下来 2𝑘2k 行描述一局游戏的过程。其中第 2𝑖−12i−1 行是兔兔的第 𝑖i 次操作(编号为 𝑖i 的操作) ,第 2𝑖2i 行是蛋蛋的第 𝑖i 次操作。每个操作使用两个整数 𝑥,𝑦x,y 来描述,表示将第 𝑥x 行第 𝑦y 列中的棋子移进空格中。

输入保证整个棋盘中只有一个格子没有棋子, 游戏过程中兔兔和蛋蛋的每个操作都是合法的,且最后蛋蛋获胜。

输出格式

输出文件的第一行包含一个整数 𝑟r,表示兔兔犯错误的总次数。

接下来 𝑟r 行按递增的顺序给出兔兔“犯错误”的操作编号。其中第 𝑖i 行包含一个整数 𝑎𝑖ai​ 表示兔兔第 𝑖i 个犯错误的操作是他在游戏中的第 𝑎𝑖ai​ 次操作。

输入输出样例

输入 #1

1 6 
XO.OXO 
1 
1 2 
1 1 

输出 #1

1
1

输入 #2

3 3 
XOX 
O.O 
XOX 
4 
2 3 
1 3 
1 2 
1 1 
2 1 
3 1 
3 2 
3 3 

输出 #2

0

输入 #3

4 4 
OOXX 
OXXO 
OO.O 
XXXO 
2 
3 2 
2 2 
1 2 
1 3 

输出 #3

2
1
2

说明/提示

对于 100%100% 的数据,1≤𝑛≤401≤n≤40,1≤𝑚≤401≤m≤40,1≤𝑘≤10001≤k≤1000。

测试点编号𝑛n𝑚m
1,21,2𝑛=1n=11≤𝑚≤201≤m≤20
33𝑛=3n=3𝑚=4m=4
4,54,5𝑛=4n=4𝑚=4m=4
6,76,7𝑛=4n=4𝑚=5m=5
88𝑛=3n=3𝑚=7m=7
9∼149∼14𝑛=2n=21≤𝑚≤401≤m≤40
15,1615,161≤𝑛≤161≤n≤161≤𝑚≤161≤m≤16
17∼2017∼201≤𝑛≤401≤n≤401≤𝑚≤401≤m≤40

Code:

#include<iostream>
#include<cstring>
using namespace std;
const int N = 50, K = 1005;
int n, m, ans, cnt, tot, head[N*N];
char g[N][N];
int match[N*N], res[K*2], win[K*2]; 
bool vis[N*N], block[N*N], color[N][N];
int dx[4] = {1, 0, 0, -1};
int dy[4] = {0, 1, -1, 0};
struct node 
{
    int to, nxt;
}edge[N*N*2*4]; //每个点连4个方向,双向边 
 
void addedge(int s, int e) 
{
    cnt++;
    edge[cnt].to = e;
    edge[cnt].nxt = head[s];
    head[s] = cnt;
    return ;
}
 
bool dfs(int x) //匈牙利算法
{
    for(int i = head[x]; i != 0; i = edge[i].nxt) 
	{
        int y = edge[i].to;
        if(block[y] == true) //添加一行屏蔽已删掉的点 
        	continue;
        if(vis[y] == false) 
		{
            vis[y] = true;
			//如果y没有匹配 或者 y的匹配点match[y]能找到一个新的匹配
            if(match[y] == 0 || dfs(match[y]) == true) 
			{
                match[y] = x; //y的配对点是x
                match[x] = y; //增加这行代码的原因是为了找最大匹配非必须点
                return true;
            }
        }
    }
    return false;
}
 
int get_id(int x, int y)
{
	return (x-1) * m + y;
} 
 
bool check(int x, int y)
{
	if(x < 1 || x > n || y < 1 || y > m || color[x][y] == false)
		return false;
	return true;	
}
 
int main()
{
	//输入 
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
			cin >> g[i][j]; //迷宫数组 
	//染色
	int sx, sy;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
        {
        	if(g[i][j] == 'O') //将白色的棋子染色 
        	{
        		color[i][j] = true;
        	}
        	else if(g[i][j] == '.') //起点记录,当作黑色对待 
        	{
        		sx = i;
        		sy = j;
			}
		}
    //建图 
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
        {
        	if(color[i][j] == true) //白子跳过 
        		continue;
        	int cur = get_id(i, j); //当前点编号 
        	for(int k = 0; k < 4; k++)
        	{
        		int nx = i + dx[k];
        		int ny = j + dy[k];
        		if(check(nx, ny) == false)
        			continue;
        		int nxt = get_id(nx, ny);
        		addedge(cur, nxt); //建边 
        		addedge(nxt, cur); //找最大匹配不需要反边,但是找最大匹配非必须点需要折返跑 
			}
		}
	//匈牙利, 目的是看起点是否落在最大匹配中 
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
        {
        	if(color[i][j] == false)
        		continue;
        	memset(vis, 0, sizeof(vis));
        	int cur = get_id(i, j); 
        	if(dfs(cur) == true) 
				ans++;
		}
	//输入游戏过程 
	int k; 
	cin >> k;
	for(int i = 1; i <= 2*k; i++)
	{
		int cur = get_id(sx, sy);
		block[cur] = true; //删掉cur 
		if(match[cur] == 0) //棋子当前不在匹配中,那么下一步会率先走到最大匹配中,必败 
			win[i] = false;
		else
		{
			int nxt =  match[cur];
			match[cur] = match[nxt] = 0;  //删掉cur与 nxt的匹配关系 
			memset(vis, 0, sizeof(vis)); //跑匈牙利记得清0 vis 
			if(dfs(nxt) == true) //若nxt还能找到匹配,则cur不是最大匹配必须, 那么下一步率先走到最大匹配,必败 
				win[i] = false;
			else
				win[i] = true; 
		}
		cin >> sx >> sy;
	}
	
	//处理输出 
    for(int i = 1; i <= k; i++) 
    {
    	if(win[2*i-1] == true && win[2*i] == true) //兔兔走之前是有必胜策略,走完蛋蛋有必胜策略 
    	{
    		res[++tot] = i; 
		}
	}
	cout << tot << endl;
	for(int i = 1; i <= tot; i++)
	{
		cout << res[i] << endl;
	}
    return 0;
}

  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值