1. 程式人生 > >2016多校訓練#1 1002 組合博弈

2016多校訓練#1 1002 組合博弈

              對於一開始接觸博弈論的同學來說,這道題的思路略難,但是如果想到了把1*20的棋盤想象成一個20位的二進位制數,然後通過sg函式預處理得到每一個二進位制數相應的sg'值,最後直接用每一行的sg值相亦或即可。注意mex陣列,也就是vis陣列可以開得大一些,在沒有確定sg'函數值範圍的情況下:

Chess

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1715    Accepted Submission(s): 748


Problem Description Alice and Bob are playing a special chess game on an n × 20 chessboard. There are several chesses on the chessboard. They can move one chess in one turn. If there are no other chesses on the right adjacent block of the moved chess, move the chess to its right adjacent block. Otherwise, skip over these chesses and move to the right adjacent block of them. Two chesses can’t be placed at one block and no chess can be placed out of the chessboard. When someone can’t move any chess during his/her turn, he/she will lose the game. Alice always take the first turn. Both Alice and Bob will play the game with the best strategy. Alice wants to know if she can win the game.
Input Multiple test cases.

The first line contains an integer T
(T100)
, indicates the number of test cases.

For each test case, the first line contains a single integer n(n1000), the number of lines of chessboard.

Then n lines, the first integer of ith line is m(m20), indicates the number of chesses on the ith line of the chessboard. Then m integers pj(1pj20)followed, the position of each chess.

Output For each test case, output one line of “YES” if Alice can win the game, “NO” otherwise.
Sample Input 2 1 2 19 20 2 1 19 1 18
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<vector>
#include<map>
#include<algorithm>
#include<queue>
#include<stack>//博弈論,求sg函式,難題 
using namespace std;
int sg[1<<21];
int bit[40];
int vis[100];//vis陣列儘量開大,防止陣列越界 
//棋子在走的過程中,二進位制的數是遞減的,因此可以利用之前陣列存的值
//來降低時間複雜度~ 
int g(int x)//sg函式 
{
	if(sg[x] != -1)
	   return sg[x];
	memset(vis, 0, sizeof(vis));
	int cnt = 0;
	int tt = x;
	while(x)
	{
		bit[++cnt] = x%2;
		x /= 2;
	}  
	int pos = -1;
	for(int i = 1; i <= cnt; i++)
	{
		if(bit[i] == 1 && pos != -1)
		   vis[sg[tt+(1<<(pos-1))-(1<<(i-1))]] = 1;//注意細節。。 
		else if(bit[i] == 0)
		   pos = i;//求最左邊有效的0的位置 
	}
	for(int i = 0; i < 100; i++)//記住,這裡要從0開始!! 
	   if(!vis[i])
	      return i;
}

void init()
{
	sg[1] = sg[0] = 0;
	for(int i = 2; i < (1<<20); i++)
	   sg[i] = g(i);
}

int main()
{
    int t;
    memset(sg, -1, sizeof(sg));
    init();
	scanf("%d", &t);
	while(t--)
	{
		int n, ans = 0;
		scanf("%d", &n);
		for(int i = 1; i <= n; i++)
		{
			int m, sum = 0; 
			scanf("%d", &m);
			while(m--)
			{
				int a;
				scanf("%d", &a);
				sum += (1<<(20-a));//把棋盤看成二進位制數 
			}
			ans ^= sg[sum];
		}
		if(ans == 0)
		   printf("NO\n");
		else
		   printf("YES\n");
	} 
	return 0;
}