HDU 1536解題報告

S-Nim

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 4985    Accepted Submission(s): 2145


Problem Description
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
 

Source
 

Recommend
LL   |   We have carefully selected several similar problems for you:  1404 1517 1524 1729 1079 

            這是簡單博弈入門的sg函數的裸題。可以直接求解。但是要弄清楚sg函數的含義。下面附上百度百科裏對sg函數的講解。感覺講的很深入。

        給定一個有向無環圖和一個起始頂點上的一枚棋子,兩名選手交替的將這枚棋子沿有向邊進行移動,無法移 動者判負。事實上,這個遊戲可以認爲是所有Impartial Combinatorial Games的抽象模型。

   也就是說,任何一個ICG都可以通過把每個局面看成一個頂點,對每個局面和它的子局面連一條有向邊來抽象成這個“有向圖遊戲”。下 面我們就在有向無環圖的頂點上定義Sprague-Grundy函數。首先定義mex(minimal excludant)運算,這是施加於一個集合的運算,表示最小的不屬於這個集合的非負整數。例如mex{0,1,2,4}=3mex{2,3,5}=0mex{}=0

對於一個給定的有向無環圖,定義關於圖的每個頂點的Sprague-Grundy函數g如下:g(x)=mex{ g(y)| yx的後繼 }

來看一下SG函數的性質。首先,所有的terminal position所對應的頂點,也就是沒有出邊的頂點,其SG值爲0,因爲它的後繼集合是空集。然後對於一個g(x)=0的頂點x,它的所有前驅y都滿足 g(y)!=0。對於一個g(x)!=0的頂點,必定存在一個後繼y滿足g(y)=0

       以上這三句話表明,頂點x所代表的postionP-position當且僅當g(x)=0(跟P-positioin/N-position 定義的那三句話是完全對應的)。我們通過計算有向無環圖的每個頂點的SG值,就可以對每種局面找到必勝策略了。但SG函數的用途遠沒有這樣簡單。如果將有 向圖遊戲變複雜一點,比如說,有向圖上並不是只有一枚棋子,而是有n枚棋子,每次可以任選一顆進行移動,這時,怎樣找到必勝策略呢?

       讓我們再來考慮一下頂點的SG值的意義。當g(x)=k時,表明對於任意一個0<=i<k,都存在x的一個後繼y滿足g(y)=i。也 就是說,當某枚棋子的SG值是k時,我們可以把它變成0、變成1、……、變成k-1,但絕對不能保持k不變。不知道你能不能根據這個聯想到Nim遊戲 Nim遊戲的規則就是:每次選擇一堆數量爲k的石子,可以把它變成0、變成1、……、變成k-1,但絕對不能保持k不變。這表明,如果將n枚棋子所在的頂 點的SG值看作n堆相應數量的石子,那麼這個Nim遊戲的每個必勝策略都對應於原來這n枚棋子的必勝策略!

       對於n個棋子,設它們對應的頂點的SG值分別爲(a1,a2,,an),再設局面(a1,a2,,an)時的Nim遊戲的一種必勝策略是把ai 變成k,那麼原遊戲的一種必勝策略就是把第i枚棋子移動到一個SG值爲k的頂點。這聽上去有點過於神奇——怎麼繞了一圈又回到Nim遊戲上了。

      其實我們還是隻要證明這種多棋子的有向圖遊戲的局面是P-position當且僅當所有棋子所在的位置的SG函數的異或0。這個證明與上節的BoutonsTheorem幾乎是完全相同的,只需要適當的改幾個名詞就行了。

       剛纔,我爲了使問題看上去更容易一些,認爲n枚棋子是在一個有向圖上移動。但如果不是在一個有向圖上,而是每個棋子在一個有向圖上,每次可以任選一個棋子(也就是任選一個有向圖)進行移動,這樣也不會給結論帶來任何變化。

所以我們可以定義有向圖遊戲的和(Sum of Graph Games):設G1G2、……、Gnn個有向圖遊戲,定義遊戲GG1G2、……、Gn的和(Sum),遊戲G的移動規則是:任選一個子遊戲Gi 並移動上面的棋子。Sprague-GrundyTheorem就是:g(G)=g(G1)^g(G2)^^g(Gn)。也就是說,遊戲的和的SG函數值是它的所有子游戲的SG函數值的異或

  再考慮在本文一開頭的一句話:任何一個ICG都可以抽象成一個有向圖遊戲。所以“SG函數”和“遊戲的和”的概念就不是侷限於有向圖遊戲。我們給每 ICG的每個position定義SG值,也可以定義nICG的和。所以說當我們面對由n個遊戲組合成的一個遊戲時,只需對於每個遊戲找出求它的每個 局面的SG值的方法,就可以把這些SG值全部看成Nim的石子堆,然後依照找Nim的必勝策略的方法來找這個遊戲的必勝策略了!

   回到本文開頭的問題。有n堆石子,每次可以從第1堆石子裏取1顆、2顆或3顆,可以從第2堆石子裏取奇數顆,可以從第3堆及以後石子裏取任意顆……我們可以把它看作3個子遊戲,第1個子遊戲只有一堆石子,每次可以取123顆,很容易看出x顆石子的局面的SG值是x%4。第2個子遊戲也是隻有一堆 石子,每次可以取奇數顆,經過簡單的畫圖可以知道這個遊戲有x顆石子時的SG值是x%2。第3個遊戲有n-2堆石子,就是一個Nim遊戲。對於原遊戲的每個局面,把三個子游戲的SG異或一下就得到了整個遊戲的SG值,然後就可以根據這個SG值判斷是否有必勝策略以及做出決策了。其實看作3個子遊戲還是保 守了些,乾脆看作n個子遊戲,其中第12個子遊戲如上所述,第3個及以後的子游戲都是“1堆石子,每次取幾顆都可以”,稱爲“任取石子游戲”,這個超簡 單的遊戲有x顆石子的SG值顯然就是x。其實,n堆石子的Nim遊戲本身不就是n個“任取石子游戲”的和嗎?

  所以,對於我們來說,SG函數與“遊戲的和”的概念不是讓我們去組合、製造稀奇古怪的遊戲,而是把遇到的看上去有些複雜的遊戲試圖分成若干個子游 戲,對於每個比原遊戲簡化很多的子游戲找出它的SG函數,然後全部異或起來就得到了原遊戲的SG函數,就可以解決原遊戲了。

       參考代碼:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<ctime>
#include<cstdlib>
#include<iomanip>
#include<utility>
#define pb push_back
#define mp make_pair
#define CLR(x) memset(x,0,sizeof(x))
#define _CLR(x) memset(x,-1,sizeof(x))
#define REP(i,n) for(int i=0;i<n;i++)
#define Debug(x) cout<<#x<<"="<<x<<" "<<endl
#define REP(i,l,r) for(int i=l;i<=r;i++)
#define rep(i,l,r) for(int i=l;i<r;i++)
#define RREP(i,l,r) for(int i=l;i>=r;i--)
#define rrep(i,l,r) for(int i=1;i>r;i--)
#define read(x) scanf("%d",&x)
#define put(x) printf("%d\n",x)
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<11
using namespace std;

int s[1010];
int sg[10100];
bool vis[10100];

int main()
{
    int k,m,n;
    while(~read(k)&&k)
    {
        REP(i,1,k)
        read(s[i]);
        sort(s+1,s+k+1);
        sg[0]=0;
        for(int i=1; i<=10000; i++)
        {
            CLR(vis);
            for(int j=1; j<=k&&s[j]<=i; j++) vis[sg[i-s[j]]]=1;
            for(int j=0; j<=10000; j++)
                if(!vis[j])
                {
                    sg[i]=j;
                    break;
                }
        }
        read(m);
        REP(i,1,m)
        {
            int h,num,cnt=0;
            read(h);
            REP(i,1,h)
            {
                read(num);
                cnt^=sg[num];
            }
            if(!cnt) printf("L");
            else printf("W");
        }
        printf("\n");
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章