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; }
}