1. 程式人生 > >AGC002 E Candy Piles 思維題 博弈

AGC002 E Candy Piles 思維題 博弈

題目連結

題意:
桌上有N 堆糖果,第i 堆糖果有Ai 個糖。兩人在玩遊戲,輪流進行,每次進行下列兩個操作中的一個
1.將當前最大的那堆糖果全部吃完
2.將每堆糖果吃掉一個
吃完的人輸,假設兩人足夠聰明,問誰能必勝 1<=n<=1e5
輸出First(表示第一個人必勝),或Second(表示第二個人必勝)

題解:
這題感覺沒有圖不好講啊,真是道很神的題啊。先放個官方題解的連結。下面的圖片來自官方題解。
官方題解

我們可以把a陣列先從大到小排序,排序後可以看作下圖,其中高度表示 a

i a_i 的數值。
圖片
我們之後可以把刪除一堆和每一堆去掉一個看作下圖:
圖片

然後我們可以把一開始的圖看作一個方格圖,把刪除一堆看作向右走,把每一堆減少一個看作向上走一步,輪廓線對應原圖中的結束狀態。如下圖所示:
圖片

對於這種博弈思想的題,經常是判斷每個狀態是先手必勝還是先手必敗,這裡也是用這種思路,我們用’x’表示先手必敗,用’o’表示先手必勝。首先顯然對於終結節點是先手必勝,因為已經所有的糖都被吃掉了。然後我們可以暴力計算其他所有點的狀態,但是這題顯然會T。我們考慮觀察這個狀態圖(如下),我們會發現,在同一條對角線上的點的狀態都是一樣的,要麼都是必勝,要麼都是必敗。那麼我們只需要一直沿著對角線走,直到走到邊緣。我們發現走到邊緣之後,如果走到當前行的最右端或者列的最右端有至少一種能用奇數步完成,那麼就是必勝的,否則就是必敗的。於是就可以快速計算這題的答案了。
圖片

程式碼:

#include <bits/stdc++.h>
using namespace std;

int n,a[100010];
int main()
{
 scanf("%d",&n);
 for(int i=1;i<=n;++i)
 scanf("%d",&a[i]);
 sort(a+1,a+n+1);
 reverse(a+1,a+n+1);
 for(int i=1;i<=n;++i)
 {
  if(i+1>a[i+1])
  {
   int ji=i+1,pd=0;
   while(a[ji]==i)
   {
    pd^=1;
    ++ji; 
   } 
   if(pd||(a[i]-i)%2==1)
   printf("First\n");
   else
   printf("Second\n");
   break;
  }
 }
 return 0;
}