原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=5724
题意:
给你n*20大小的棋盘。每一行给你几个棋子。你可以选任意一个棋子向右移动,如果右边有棋子那就移到右边最近的空位上。
分析:
这题就是考sg数组的应用吧。当棋子的位置和数量一定时,结果就是定的。同时棋盘的列数是固定的,那总共放棋子的可能只有2^20-1,大概100万吧。所以可以存下。然后每一行的游戏是独立的,所以把每一行的SG值异或一下就好了。特别注意的是,题目中给的棋子的位置是从左往右数第几列的,但我们把一行看成一个二进制时,最右边是最小的数位。
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int sg[5000000];
int main()
{
int t;
sg[0]=0;
sg[1]=0;
for(int i=2;i<1<<20;i++) //预处理判断所有数的sg值
{
int a=i;
int g=1;
int num=0;//表示i的二进制表示中一的个数
int wei=0;
int sum[20];//记录i二进制表示中有一的数位
int k[20];//记录i的二进制表示
int op[30];//记录所有灯走到的sg值
memset(k,0,sizeof(k));
memset(op,0,sizeof(op));
op[0]=-1;
while(a>0)
{
if(a%2==1)
{
sum[num++]=wei;
k[wei]=1;}
wei++;
a/=2;
}
if(num==wei)//对对二进制1、11、111等特殊处理
{
sg[i]=0;
continue;
}
for(int j=0;j<num;j++)//对每个有1的数位进行一次找能到它的数
{
a=i;
int b=sum[j];
b--;
while(k[b]!=0)/找最近的非1数位
{
b--;
if(b<0)
break;
}
if(b<0)//没有结束这次循环
continue;
a-=1<<sum[j];
a+=1<<b;
op[g++]=sg[a];
}
sort(op,op+g);
for(int k=1;k<g;k++)
{
if(op[k]-op[k-1]>1)
{
sg[i]=op[k-1]+1;
break;
}
if(k==g-1)
{
sg[i]=op[k]+1;
}
}
}
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
int ans=0;
int s=0;
for(int i=0;i<n;i++)
{
scanf("%d",&s);
int y=0;
int z;
for(int k=0;k<s;k++)
{
scanf("%d",&z);
y+=1<<(20-z);
}
ans^=sg[y];
}
if(ans)
printf("YES\n");
else
printf("NO\n");
}
}
Author : Casual@Acpp