22牛客多校6 - Z-Game on grid(博弈,反推)

https://ac.nowcoder.com/acm/contest/33191/M

题意
给定一个 n*m 的棋盘,每个位置有一个字符,共有 'A', 'B', '.' 三种字符。
在 (1, 1) 位置上有一个棋子,Alice 和 Bob 轮流移动这个棋子,每次移动只能向右或者向下走一个单位。

如果走到标有字符 ‘A’ 的格子,那么 Alice 胜利;如果走到 ‘B’,Bob 胜利;如果走到最右下角的格子,平局。

现在 Alice 想知道,自己是否在无论 Bob 如何操作的情况下都能 win? 都能 draw? 都能 lose?

1 ≤ N , M ≤ 500 1≤N,M≤500 1N,M500

思路
首先需要发现,如果一个格子的坐标之和为偶数的话,那么这个位置轮到 Alice 操作;否则 Bob 操作。

棋子只能向右或者向下走,如果轮到 Alice 操作:

  • 发现右边的点或者下面的点是必胜点,那么 Alice 一定会走向必胜点,那么当前点也成为必胜点。
  • 否则当前点是不确定点。

而 Bob 会阻止 Alice 达到其想要的状态:

  • 但是如果要走的两个点都是必胜点的话,Bob 没有选择,那么当前点也会成为必胜点。
  • 否则当前点是不确定点。

从最右下角往前推,把所有的必胜点都推出来,如果发现起点是必胜点,那么就一定能得到想要的状态;
否则就不能得到。

从后往前推只需要从最后一行到第一行,对于每一行从后往前即可,这样遍历到一个点时,其右边位置和下面位置都已经遍历确定过了。

Code

#include<bits/stdc++.h>
using namespace std;

#define Ios ios::sync_with_stdio(false),cin.tie(0)
#define endl '\n'

const int N = 2010, mod = 1e9+7;
int T, n, m;
char a[N][N], f[N][N];

signed main(){
	Ios;
	cin >> T;
	while(T--)
	{
		cin >> n >> m;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				cin >> a[i][j];
		
		for(int k=n;k>=1;k--)
		{
			for(int j=m;j>=1;j--)
			{
				if(a[k][j] != '.' || k==n&&j==m) f[k][j] = a[k][j];
				else if((k+j) % 2 == 0) //Alice走
				{
					//要走的点存在必胜点,当前点为必胜点
					if(j+1<=m && f[k][j+1] == 'A' || k+1<=n && f[k+1][j] == 'A') f[k][j] = 'A'; 
					else f[k][j] = '#';
				}
				else //Bob走
				{
					//要走的点都是必胜点,当前点为必胜点
					if((j+1 > m || f[k][j+1] == 'A') && (k+1 > n || f[k+1][j] == 'A')) f[k][j] = 'A'; 
					else f[k][j] = '#';
				}
			}
		}
		if(f[1][1] == 'A') cout << "yes ";
		else cout << "no ";
		
		for(int k=n;k>=1;k--)
		{
			for(int j=m;j>=1;j--)
			{
				if(a[k][j] != '.' || k==n&&j==m) f[k][j] = a[k][j];
				else if((k+j) % 2 == 0)
				{
					if(j+1<=m && f[k][j+1] == '.' || k+1<=n && f[k+1][j] == '.') f[k][j] = '.';
					else f[k][j] = '#';
				}
				else
				{
					if((j+1 > m || f[k][j+1] == '.') && (k+1 > n || f[k+1][j] == '.')) f[k][j] = '.';
					else f[k][j] = '#';
				}
			}
		}
		if(f[1][1] == '.') cout << "yes ";
		else cout << "no ";
		
		for(int k=n;k>=1;k--)
		{
			for(int j=m;j>=1;j--)
			{
				if(a[k][j] != '.' || k==n&&j==m) f[k][j] = a[k][j];
				else if((k+j) % 2 == 0)
				{
					if(j+1<=m && f[k][j+1] == 'B' || k+1<=n && f[k+1][j] == 'B') f[k][j] = 'B';
					else f[k][j] = '#';
				}
				else
				{
					if((j+1 > m || f[k][j+1] == 'B') && (k+1 > n || f[k+1][j] == 'B')) f[k][j] = 'B';
					else f[k][j] = '#';
				}
			}
		}
		if(f[1][1] == 'B') cout << "yes ";
		else cout << "no ";
		
		cout << endl;
	}
	
	return 0;
}

经验

  • 必胜点
  • 反推
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值