1. 程式人生 > >【bzoj3895】【取石子】【博弈論+記憶化搜尋】

【bzoj3895】【取石子】【博弈論+記憶化搜尋】

Description

Alice和Bob兩個好朋含友又開始玩取石子了。遊戲開始時,有N堆石子 排成一排,然後他們輪流操作(Alice先手),每次操作時從下面的規則中任選一個: ·從某堆石子中取走一個 ·合併任意兩堆石子 不能操作的人輸。Alice想知道,她是否能有必勝策略。

Input

第一行輸入T,表示資料組數。 對於每組測試資料,第一行讀入N。 接下來N個正整數a1,a2…an,表示每堆石子的數量。

Output

對於每組測試資料,輸出一行。 輸出YES表示Alice有必勝策略,輸出NO表示Alice沒有必勝策略。

Sample Input

2
3
1 1 2
2
3 4
3
2 3 5

Sample Output

YES
NO
NO

HINT

100%的資料滿足T<=100,  N<=50. ai<=1000 題解:考慮如果不存在石子數為1的堆。           設這種狀態下運算元為x,顯然x等於石子總數加運算元減1。           可以證明當x為奇數時先手必勝。當x為偶數時先手必敗。           如果只有1堆石子,該結論顯然成立。           如果有多堆石子,每堆石子個數都大於1,並且x為偶數,下面我們證明這樣先手必敗。           1.如果先手選擇合併兩堆石子,那麼每堆石子的個數依然大於1,x變為奇數。           2.如果先手選擇從一堆石子數大於2的堆中拿走一枚石子,那麼同上每堆石子個數依然大於1,x變為奇數。 
          3.如果先手選擇從一堆石子數等於2的堆中拿走一枚石子,那麼後手可以合併剩下的1枚石子到任意一個堆。           那樣x的奇偶性不變,每堆石子的個數依然大於1.           綜上所述,結論成立。           然後考慮如果存在石子數為1的堆.我們設石子數為1的堆的個數為x,其餘堆的個數為y.           令f[x][y]為這種狀態下先手必勝或是必敗。           每次轉移列舉所有可能的操作。記憶化搜尋即可。 程式碼:         
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,f[55][50055],T,x,a,b,vis[55][50055];
int dfs(int a,int b){
  if (a==0) return b&1;
  if (b==1) return dfs(a+1,0);
  if (vis[a][b]) return f[a][b];vis[a][b]=1;
  if (a&&!dfs(a-1,b)) return f[a][b]=1;
  if (a&&b&&!dfs(a-1,b+1)) return f[a][b]=1;
  if (a>=2&&!dfs(a-2,b+2+(b?1:0))) return f[a][b]=1;
  if (b&&!dfs(a,b-1)) return f[a][b]=1;
  return f[a][b]=0;
}
int main(){
  scanf("%d",&T);
  while (T--){
    scanf("%d",&n);a=0;b=-1;
    for (int i=1;i<=n;i++){
       scanf("%d",&x);
       if (x==1) a++;
       else b+=x+1;
    }
   if (b==-1) b=0;
   if (dfs(a,b)==1) printf("YES\n");
   else printf("NO\n");
  }	
}