P2575 高手过招

Posted Jozky86

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2575 高手过招相关的知识,希望对你有一定的参考价值。

P2575 高手过招

题意:

AKN玩游戏玩累了,于是他开始和同伴下棋了,玩的是跳棋!对手是wwx!这两位上古神遇在一起下棋,使得棋局变得玄幻莫测,高手过招,必有一赢,他们都将用最佳策略下棋,现在给你一个n*20的棋盘,以及棋盘上有若干个棋子,问谁赢?akn先手!

游戏规则是这样的:

对于一个棋子,能将它向右移动一格,如果右边有棋子,则向右跳到第一个空格,如果右边没有空格,则不能移动这个棋子,如果所有棋子都不能移动,那么将输掉这场比赛。

题解:

注意题目意思,题目说的是每个棋子只能向右移动一格,也就是说棋盘的每一行是独立的,所以我们求出每一行的sg值,然后全部异或起来
对于每一行我们来分析:根据题目意思我们可以得知,一次操作中,棋子是跳到右侧第一个空格处,如果右侧没有空格就说明该棋子不能动,所以我们从右侧开始,每次找到遇到空格就记录,然后往左找,找到棋子,就将该棋子跳到记录的空格上,相当于执行了一步操作,得到新状态,然后查找新状态的答案,终状态就是所有棋子都不能动,输掉游戏
我们利用mex运算来求sg函数,对于一个状态x,我们求出x的后继局面的所有sg值,根据mex运算可以求出sg[x]的值。
状态x我们可以用二进制来实现,因为每个位置只有存在棋子和不存在棋子两个情况
这个题很不错,很考验思维,解法也很妙
复杂度:状态数 * 转移

代码:

#include<bits/stdc++.h>
#define debug(a,b) printf("%s = %d\\n",a,b);
typedef long long ll;
using namespace std;

inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);
   return s*w;
}
const int maxn=2e6+9;
int sg[maxn];
int t,n;
int dfs(int x){
	if(sg[x]!=-1)return sg[x];
	int vis[21];
	memset(vis,0,sizeof(vis));
	
	int last0=-1;//最近的一个0的位置 
	for(int j=19;j>=0;j--){
		if((x>>j)&1){//如果第j位有棋子 
			if(last0!=-1){//如果右侧有0, 
				vis[dfs(x^(1<<j)^(1<<last0))]=1;
				//棋子移动一步到空格位置 
			}
		}
		else last0=j;
	}
	for(int i=0;i<=20;i++){
		if(vis[i]==0)return sg[x]=i;
	}
} 
int main()
{
	
	cin>>t;
	memset(sg,-1,sizeof(sg));
	while(t--){
		cin>>n;
		int ans=0;
		for(int i=1;i<=n;i++){
			int x=0,col,m;
			cin>>m;
			while(m--){
				cin>>col;
				col--; 
				x|=(1<<col);
				//最左边是第零列 
			}
			ans^=dfs(x);
		}
		if(ans==0)cout<<"NO"<<endl;
		else cout<<"YES"<<endl; 
	}
}

以上是关于P2575 高手过招的主要内容,如果未能解决你的问题,请参考以下文章

P2575 高手过招

洛谷 [P2575] 高手过招

某大佬的TODOLIST

高手过招 放“码”出击 | 2022 Google 全球编程比赛集结倒计时!

Luogu 2575 高手过招-SG函数

LuoguP2575 高手过招(博弈论)