S-Nim(fromHDU)(博弈问题)(Sprague-Grundy定理)

题目描述:
Arthur and his sister Caroll have been playing a game called Nim for some time now. Nim is played as follows:

The starting position has a number of heaps, all containing some, not necessarily equal, number of beads.

The players take turns chosing a heap and removing a positive number of beads from it.

The first player not able to make a move, loses.

Arthur and Caroll really enjoyed playing this simple game until they recently learned an easy way to always be able to find the best move:

Xor the number of beads in the heaps in the current position (i.e. if we have 2, 4 and 7 the xor-sum will be 1 as 2 xor 4 xor 7 = 1).

If the xor-sum is 0, too bad, you will lose.

Otherwise, move such that the xor-sum becomes 0. This is always possible.

It is quite easy to convince oneself that this works. Consider these facts:

The player that takes the last bead wins.

After the winning player’s last move the xor-sum will be 0.

The xor-sum will change after every move.

Which means that if you make sure that the xor-sum always is 0 when you have made your move, your opponent will never be able to win, and, thus, you will win.

Understandibly it is no fun to play a game when both players know how to play perfectly (ignorance is bliss). Fourtunately, Arthur and Caroll soon came up with a similar game, S-Nim, that seemed to solve this problem. Each player is now only allowed to remove a number of beads in some predefined set S, e.g. if we have S =(2, 5) each player is only allowed to remove 2 or 5 beads. Now it is not always possible to make the xor-sum 0 and, thus, the strategy above is useless. Or is it?

your job is to write a program that determines if a position of S-Nim is a losing or a winning position. A position is a winning position if there is at least one move to a losing position. A position is a losing position if there are no moves to a losing position. This means, as expected, that a position with no legal moves is a losing position.
Input
Input consists of a number of test cases. For each test case: The first line contains a number k (0 < k ≤ 100 describing the size of S, followed by k numbers si (0 < si ≤ 10000) describing S. The second line contains a number m (0 < m ≤ 100) describing the number of positions to evaluate. The next m lines each contain a number l (0 < l ≤ 100) describing the number of heaps and l numbers hi (0 ≤ hi ≤ 10000) describing the number of beads in the heaps. The last test case is followed by a 0 on a line of its own.
Output
For each position: If the described position is a winning position print a ‘W’.If the described position is a losing position print an ‘L’. Print a newline after each test case.
Sample Input
2 2 5
3
2 5 12
3 2 4 7
4 2 3 7 12
5 1 2 3 4 5
3
2 5 12
3 2 4 7
4 2 3 7 12
0
Sample Output
LWW
WWL

今天复习博弈论有关问题,我觉得没有什么能比把一个原先看起来既复杂又神奇的东西搞懂更加有意思的了。(从学习一样东西到讲解,真是感慨)今天的Sprague-Grundy定理就是一个,是nim问题的一般性推广(加强版)。
首先讲nim问题,典型问题就是:
有两个玩家;
有三堆棋子(比如:可以分别是 5,7,9个);
游戏双方轮流操作;
玩家的每次操作是选择其中某一堆棋子,然后从中取走任意个;
最后一次取棋子的一方为获胜方;

此类问题的特点是 游戏人数是二人,局面上的棋子双方都可操作,是属于公平游戏(公平游戏就是指游戏的一方要么赢要么输)。
这个问题该如何解决?
有一个定理:我们把上述的数字化为二进制
5:101
7:111
9:1001
我们求他们的异或值,设nim=5⊕7⊕9,则我们把nim这个值称之为尼姆值,如果这个值为0则先手必输,如果为正,先手必赢(假设双方都按最优步骤)。
但是,今天的这个问题就不太一样 ,如果我们说每次只能制定拿若干个棋子,假设只能拿2个或者5个,那么题目就不一样了,这就是今天这个S-Nim这道题了。
这里要引入Sprague-Grundy定理:对一个公平游戏 的状态,其等效游戏的 Nim 数,就等于所有其后继状态 Nim 数的 Mex 函数值
Mex 函数值:这个函数的接受一个自然数集 N,输出自然数集中减去 N 之后最小的那个整数。例如 Mex({0,1,3,}) = 2, Mex({1,2,}) = 0, Mex({}) = 0。(所有定理不证明)
这道题就是典型的应用这个定理
首先我们先可以看只有一个堆设有7个,假设只能拿1个或者3个,那么7的状态会有两个 6和4,所以(我们设堆里的棋子个数为状态,nim(x)为状态x的尼姆值),则nim(7)=Mex({nim(6),nim(4)})
那么假设我们有n个堆,则nim=nim(x1)⊕nim(x2)⊕….⊕nim(xn)

ac代码:

import java.util.Arrays;
import java.util.HashSet;

import java.util.Scanner;

public class Main 
{   
    static int sg[]=new int[10005];//我们用sg来存储各个状态的nim值
    static int s,c[];
    public static void main(String[] args) 
    {
        Scanner sc=new Scanner(System.in);
        for(;;)
        {
            s=sc.nextInt();//能拿的种类个数
            if(s==0)break;
            Arrays.fill(sg, -1);//记忆化标志
            c=new int[s];//存放在c数组里
            for(int i=0;i//排序
            for(int i=0;i0];i++)
                sg[i]=0;//那么显然,0到小于c[0]的nim值都为0,也是边界
            int m=sc.nextInt();
            while((m--)>0)
            {
                int t=sc.nextInt();
                int nim=0;
                for(int i=0;iint k=sc.nextInt();
                    nim^=getSG(k);//每次输入就做一次异或运算
                }
                if(nim==0)//直接判断即可
                    System.out.print("L");
                else
                    System.out.print("W");
            }
            System.out.println();
            Arrays.fill(sg, -1);
        }
    }
    static int getSG(int x)
    {
        if(sg[x]!=-1)return sg[x];
        HashSet set=new HashSet<>();//这里用HashSet的原因是,既然是集合,就不允许出现重复的自然数
        for(int i=0;iif(x>=c[i])//获得所有可能的子状态,将其存入set中
                set.add(getSG(x-c[i]));//
            else
                break;              
        }
        return sg[x]=Mex(set);
    }
    static int Mex(HashSet set)//按照定义,找出nim值
    {
        int x=0;
        for(;;)
        {
            if(!set.contains(x))
                break;
            x++;
        }
        return x;
    }
}

你可能感兴趣的:(算法分析,博弈论,nim)