hdu 3032 Nim or not Nim?

题目大意:

Alice和Bob轮流取N堆石子,每堆S[i]个,Alice先,每一次可以从任意一堆中拿走任意个石子,也可以将一堆石子分为两个小堆。先拿完者获胜。(1 ≤ N ≤ 10^6, 1 ≤ S[i] ≤ 2^31 - 1)

解题思路:

对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Grundy函数g如下:g(x)=mex{ g(y) | y是x的后继 },这里的g(x)即sg[x]
例如:取石子问题,有1堆n个的石子,每次只能取{1,3,4}个石子,先取完石子者胜利,那么各个数的SG值为多少?
sg[0]=0,
n=1时,可以取走{1}个石子,剩余{0}个,mex{sg[0]}={0},故sg[1]=1;
n=2时,可以取走{1}个石子,剩余{1}个,mex{sg[1]}={1},故sg[2]=0;
n=3时,可以取走{1,3}个石子,剩余{2,0}个,mex{sg[2],sg[0]}={0,0},故sg[3]=1;
n=4时,可以取走{1,3,4}个石子,剩余{3,1,0}个,mex{sg[3],sg[1],sg[0]}={1,1,0},故sg[4]=2;
n=5时,可以取走{1,3,4}个石子,剩余{4,2,1}个,mex{sg[4],sg[2],sg[1]}={2,0,1},故sg[5]=3;
以此类推.....
     x  0  1  2  3  4  5  6  7  8....
sg[x] 0  1  0  1  2  3  2  0  1....

所以,对于这题:

sg[0]=0

sg[1]=mex{sg[0] }=1

sg[2]=mex{sg[0],sg[1],sg[1,1] }=mex{0,1,1^1}=2;

sg[3]=mex{sg[0],sg[1],sg[2],sg[1,2]}=mex{0,1,2,1^2}=mex{0,1,2,3}=4;

sg[4]=mex{sg[0],sg[1],sg[2],sg[3],sg[1,3],sg[2,2]}=mex{0,1,2,4,5,0}=3;

              ..............................................................................

可以发现:sg[4*k+1]=4*k+1,sg[4*k+2]=4*k+2,  sg[4*k+3]=4*k+4,sg[4*k+4]=4*k+3

sg函数打表程序

#include <iostream> 
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
int main() {
	int i, j;
	int sg[1010], mex[1010];
	sg[0] = 0, sg[1] = 1;
	for(i = 2; i <= 1000; i++) {
		memset(mex, 0, sizeof mex);
		for(j = 1; j <= i/2; j++) {
			mex[sg[j]^sg[i - j]] = 1;
		}
		for(j = 1; j <= i; j++) {
			mex[sg[i - j]] = 1;
		}
		for(j = 0; ; j++) {
			if(mex[j] == 0) {
				sg[i] = j;
				break;
			}
		}
	}
	for(i = 0; i <= 100; i++) {
		printf("sg[%d] : %d\n", i, sg[i]);
	}
	return 0;
}

#include<stdio.h>
using namespace std;

int sg(int x){
	if(x%4==0)
	return x-1;
	if(x%4==3)
	return x+1;
	else return x;
}
int main(){
	
	int t;
	scanf("%d",&t);
	while(t--){
		int n;
		scanf("%d",&n);
		int temp=0;
		for(int i=0;i<n;i++)
		{
			int x;
			scanf("%d",&x);
			temp^=sg(x);
		}
		if(temp)
		printf("Alice\n");
		else printf("Bob\n");
	}
	
	return 0;
}


猜你喜欢

转载自blog.csdn.net/l2533636371/article/details/80181690