sg函数+nim博弈+打表_______A Simple Nim(hdu 5795 2016多校第六场)

来源:互联网 发布:视频直播源码 编辑:程序博客网 时间:2024/05/18 16:57

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 $1\le T\le 100$, 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[n-1]$, representing heaps with $s[0],s[1], ...,s[n-1]$ objects respectively.$(1\le n\le 10^6,1\le s[i]\le 10^9)$
 

Output
For each test case,output a line whick contains either"First player wins."or"Second player wins".
 

Sample Input
224 431 2 4
 

Sample Output
Second player wins.First player wins.
 

题目意思:


一共有n堆石子,每堆石子个数已知。a,b轮流执行操作a或者b:a操作是从任意某一堆石子中取任意个石子,可以取完但不能不取。b操作是选择任意一堆石子任意分成三堆,并且满足三堆石子个数都大于0且之和等于原来堆的个数。当轮到某人开始操作时候,已经没有石子就判定该人输。



分析:


如果去掉分堆这个操作,那么这题目就是赤裸裸的nim游戏原型,那么只需要计算所有堆的个数异或值即可。
如果某堆石子有7个,
那么在nim原型下:
这堆石子经过一次操作后只可能变为6,5,4,3,2,1,0其中一个。
但是如果加入分堆的操作后:
这堆石子就可以变为:6,5,4,3,2,1,0,124,133,223
由nim游戏的策略我们知道:124 三堆等价于 1^2^4 = 7 一堆。所以说我们可以这样来看,加入分堆的操作后,假如某堆的个数为k,那么可以等价为nim游戏模型的sg[k]个。
还是以7为例:7可以分为1,2,4,
i.所以7可以转化为sg[1] ^ sg[2] ^ sg[4].因为 1,2 不能使用分堆操作,那么1,2肯定等价于nim游戏模型的1,2所以sg[1] = 1,sg[2] = 2 。
现在我们要求sg[4]。在可分堆中,4 不仅可以通过拿转化为3,2,1,0,还可以通过分堆变为112。
我们知道112其实就是sg[1]^sg[1]^sg[2] = 2。意思就是4分堆112后就等价于nim游戏模型的2。
ii.对于4我们知道可以转化为3,2,1,0,'2'.注意,前面的,3,2,1,0是可分堆下的,最后的2 是nim游戏下的等价个数。
所以我们需要将前面3,2,1,0也转化为nim游戏下的等价个数,也就是求sg[3],sg[2].sg[1],sg[0]。显然,sg[2]=2,sg[1]=1,sg[0]=0.
iii.那么对于3,我们知道可以通过拿转化为2,1,0,也可以通过分堆为111,我们可以直接通过求sg知道3可以转化为nim游戏模型的sg[2],sg[1],sg[0],sg[1]^sg[1]^sg[1]化简就是2,1,0.
那么很显然在nim游戏中能转化为2,1,0的就是3.所以sg[3] = 3.我们回到ii.因为4 可以转化为sg[3],sg[2],sg[1],sg[0],sg[1]^sg[1]^sg[2].带入sg[3]化简得到3,2,1,0.那么很显然在nim游戏中能转化为3,2,1,0的就是4,所以sg[4]=4.我们回到i.因为7可以转化为sg[6],sg[5],sg[4],sg[3],sg[2],sg[1],sg[1],sg[1]^sg[2]^sg[4],sg[1]^sg[3]^sg[3],sg[2]^sg[2]^sg[3].
通过相同的步骤我们可以计算得到sg[6] = 6,sg[5] = 5那么sg[1]^sg[2]^sg[4] = 7,sg[1]^sg[3]^sg[3] = 1,sg[2]^sg[2]^sg[3] = 3.所以7可以转化为nim游戏中的7,6,5,4,3,2,1,0.在nim游戏中能转化为7,6,5,4,3,2,1,0的就是8,所以sg[7] = 8。

这样通过以上的规则我们可以求出在可分堆条件下任何数在nim游戏原型的等价个数。这样我们就可以将这个游戏直接转化为nim游戏。这样就可以同nim游戏的策略来做。以上的方法是使用递归的方式求解。时间复杂度较高,如果加入记忆化能够将时间压缩到0(n).但是我们通过打表可以得到规律。



打表代码:

#include <bits/stdc++.h>using namespace std;int g[1010];void init() {    memset(g, -1, sizeof(g));}int getSG(int x) {    if(g[x] != -1) return g[x];    if(x == 0) return 0;    if(x == 1) return 1;    if(x == 2) return 2;    int vis[110];    memset(vis, 0, sizeof(vis));    for(int i = 1; i < x; i++) {        int t = 0;        int a = getSG(i);        vis[a] = 1;        for(int j = 1; j < x - i; j++) {            int b = getSG(j);            int c = getSG(x - i - j);            vis[a^b^c] = 1;        }    }    vis[0] = 1;    for(int i = 0; ; i++) if(!vis[i])        return g[x] = i;}int main() {    int n;    init();    for(int i = 1; i <= 100; i++) {        g[i] = getSG(i);        printf("%d %d %d\n", i, i % 8, g[i]);    }}

发现规律:
对于一个数K,求sg[K]。
如果 K%8 == 0 则 sg[K] = K - 1;
如果 K%8 == 7 则 sg[K] = K + 1;
否则 sg[K] = K.


ac代码:

#include <bits/stdc++.h>using namespace std;const int maxn = 1e6 + 10;int getSG(int x) {    if(x == 0)        return 0;    if(x % 8 == 0)        return x - 1;    if(x % 8 == 7)        return x + 1;    return x;}int a[maxn];int main() {    int t;    scanf("%d", &t);    while(t--) {        int n;        scanf("%d",&n);        int ans = 0;        for(int i = 1; i <= n; i++) {            scanf("%d", &a[i]);            ans ^= getSG(a[i]);        }        if(!ans)            puts("Second player wins.");        else            puts("First player wins.");    }}



关于nim博弈详情点这里









题目意思:

0 0
原创粉丝点击