HDU 5795 A Simple Nim 详解(SG打表找规律,博弈好题)

    xiaoxiao2021-12-14  20

    A Simple Nim

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 973    Accepted Submission(s): 569 Problem Description Two players take turns picking candies from n heaps,the player who picks the last one will win the game.On each turn they can pick any number of candies which come from the same heap(picking no candy is not allowed).To make the game more interesting,players can separate one heap into three smaller heaps(no empty heaps)instead of the picking operation.Please find out which player will win the game if each of them never make mistakes.   Input Intput contains multiple test cases. The first line is an integer  1T100 , the number of test cases. Each case begins with an integer n, indicating the number of the heaps, the next line contains N integers  s[0],s[1],....,s[n1] , representing heaps with  s[0],s[1],...,s[n1]  objects respectively. (1n106,1s[i]109)   Output For each test case,output a line whick contains either"First player wins."or"Second player wins".   Sample Input 2 2 4 4 3 1 2 4   Sample Output Second player wins. First player wins.   Author UESTC   Source 2016 Multi-University Training Contest 6   Recommend wange2014   Statistic |  Submit |  Discuss |  Note

    题意:给定n堆石子,每次有两种操作,1是从任意一堆中取任意个,2是把一堆分为三堆且这三堆都不为空,问谁能赢。 题解:首先看题目的数据范围是1e9,那么算出每个sg肯定会超时,所以我们想到打表找规律。首先我们暴力打表算出200以内的sg函数值,对于一堆石子来说处理出他的所有后继情况。如果只是取石子的话,那么sg函数很容易算,但如果分成三堆呢?那sg函数怎么算?那就类比尼姆博弈啊,这一堆x可以到达的状态是,分成任意三堆,这三堆相当于一个尼姆博弈,求他们的值就是抑或起来。。或者可以这样想,一堆数量x分成三堆,相当于游戏又分成了三小堆,为了求x的sg值,肯定是这三小堆(x的后继)sg抑或起来,变成了一个局部的sg基础题(可以参考HDU 1848做法 http://blog.csdn.net/qq_34374664/article/details/53432254 一种打表方式: #include <iostream> #include <algorithm> #include <cstdio> #include <cstring> using namespace std; const int maxn = 1e6 + 5; int sg[maxn], book[maxn]; void init() { for(int i = 1; i <= 200; i++) { memset(book, 0, sizeof(book)); for(int x = 0; x < i; x++) //这是取完之后剩下的堆数,即可到达的地方 book[sg[x]] = 1; for(int j = 1; j < i; j++) //枚举三堆所有的可能性,也是可到达的地方,分成三堆要^ { for(int jj = 1; jj < i; jj++) { if(i-j-jj > 0) book[sg[i-j-jj]^sg[j]^sg[jj]] = 1; } } for(int j = 0; book[j]; j++) //注意找mex时候j从0开始。。 { sg[i] = j + 1; } } for(int i = 0;i <= 200; i++) printf("sg[%d] : %d\n" ,i, sg[i]); } int main() { // init(); int t, n, x; scanf("%d", &t); while(t--) { scanf("%d", &n); int ans = 0; for(int i = 1; i <= n; i++) { scanf("%d", &x); if(x%8 == 0) ans ^= x - 1; else if(x%8 == 7) ans ^= x + 1; else ans ^= x; } if(ans) printf("First player wins.\n"); else printf("Second player wins.\n"); } return 0; } 另一种打表方式: #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAXN = 1e3+5; typedef long long LL; int sg[MAXN]; int get_SG(int x) { if(sg[x]!=-1) //记忆化一下 return sg[x]; int hash[MAXN]; memset(hash, 0, sizeof(hash)); for(int i=x-1; i>=0; i--) hash[get_SG(i)]=1; for(int j=1; j<=x; j++) { for(int jj=1; jj<=x; jj++) { if(x-j-jj > 0) hash[get_SG(x-j-jj)^get_SG(j)^get_SG(jj)]=1; //把分成三小堆当成每一堆去想 } } int k; for(k=0; k<MAXN; k++) { if(!hash[k]) { return sg[x] = k; } } } int main() { /**测试打表**/ /*memset(sg,-1,sizeof(sg)); for(int i=0; i<50; i++) { printf("sg[%d] = %d\n",i,get_SG(i)); }*/ int T; cin>>T; while(T--) { int m; LL ans = 0; cin>>m; while(m--) { LL x; cin>>x; if(x%8==0) ans ^= (x-1); else if(x%8 == 7) ans ^= (x+1); else ans ^= x; } if(ans) puts("First player wins."); else puts("Second player wins."); } return 0; }

    转载请注明原文地址: https://ju.6miu.com/read-963021.html

    最新回复(0)