1. 程式人生 > >4.1.7 Cutting Game(POJ 2311)

4.1.7 Cutting Game(POJ 2311)

Problem description:

  兩個人在玩如下游戲。

  準備一張分成 w*h 的格子的長方形紙張,兩人輪流切割紙張。要沿著格子的邊界切割,水平或者垂直地將紙張切成兩部分。切割了n次之後就得到了n+1張紙,每次都選擇切得的某一張紙再進行切割。首先切出只有一個格子的紙張(1*1的各自組成的紙張)的一方獲勝。當雙方都採取最優策略時,先手是必勝,還是必敗?

  2<=w,h<=200

Input:

  w=2,  h=2

Out put:

  LOSE

  前面的硬幣問題2中,有n堆硬幣,我們求出每堆硬幣的Grundy值,再根據它們XOR後的值判斷勝負。另一方面,這個遊戲中,初始只有一張紙,紙張的數量隨切割增加。這樣會發生分割的遊戲,也能夠計算Grundy值(XOR運算滿足結合律)。

  當w*h的紙張分成兩張時,假設所分得的紙張的Grundy值分別為g1和g2,則這兩張紙對應的狀態的Grundy值可以表示為g1 XOR g2。

  在Nim中,不論有幾堆石子,初始狀態是怎麼樣的,只有XOR的結果相同,那麼對勝負是沒有影響的,這裡也是同樣的,只要Grundy值相同,即便發生切割,只要對分割後的各部分取XOR,就可以用這一個Grundy值來代表幾個遊戲複合而成的狀態,Grundy值也可以同樣計算。

  瞭解了會發生分割的遊戲的處理方法之後,只要像之前的問題一樣,列舉所有一步能轉移到的狀態的Grundy值,就能夠計算Grundy值了。

  另外,切割紙張時,一旦切割出了長或寬為1 的紙張,下一步就一定能夠切割出1*1的紙張,所以可以知道此時必敗。因此,切割紙張時,總要保證長和寬至少為(無論如何都不能保證是,就是必敗態。此時根據Grundy值的定義,不需要特別處理其Grundy值也是)。

const int MAX_WH =200;
//記憶化搜尋所用的陣列,程式開始執行時全部初始化為-1
int mem[MAX_WH][MAX_WH];
int grundy(int w,int h){
    if(mem[W][H]!=-1) return mem[w][h];
    set<int> s;
    for(int i=2;w-i>=2;i++)
        s.insert(grundy(i,h)^grundy(w-i,h));
    for(int i=2;h-i>=2;i++)
        s.insert(grundy(w,i)
^grundy(w,h-i)); int res=0; while(s.count(res)) res++; return mem[w][h]=res; } void solve(int w,int h){ if(grundy(w,h)!=0) puts("WIN"); else puts("LOSE"); }
View Code