1. 程式人生 > >[AGC002E]Candy Piles-博弈論

[AGC002E]Candy Piles-博弈論

Candy Piles

Problem Statement

There are N piles of candies on the table. The piles are numbered 1 through N. At first, pile i contains ai candies.

Snuke and Ciel are playing a game. They take alternating turns. Snuke goes first. In each turn, the current player must perform one of the following two operations:

Choose a pile with the largest number of candies remaining, then eat all candies of that pile.
From each pile with one or more candies remaining, eat one candy.
The player who eats the last candy on the table, loses the game. Determine which player will win if both players play the game optimally.

Constraints

1≤N≤105
1≤ai≤109

Input

The input is given from Standard Input in the following format:

N
a1 a2 … aN

Output

If Snuke will win, print First. If Ciel will win, print Second.

Sample Input 1

2
1 3

Sample Output 1

First

At the beginning of the game, pile 2 contains the most candies. If Snuke eats all candies of this pile, Ciel has no choice but to eat the last candy.

Sample Input 2

3
1 2 1

Sample Output 2

First

If Snuke eats one candy from each pile, Ciel is again left with the last candy.

Sample Input 3

3
1 2 3

Sample Output 3

Second

楊氏圖表???
好像在哪裡見過……
然而並不是什麼有用的東西……

題意:
n堆糖果,每堆分別有a1,a2...an顆糖。
每次一個人可以選擇從每一堆裡吃一顆糖,或者選擇最大的一堆糖吃掉。
求誰能贏。

思路:
按a排序。
然後就得到了一張這樣的東西,如果喜歡的話你甚至可以叫他“楊氏圖表”:

000
0000
000000000
000000000000
000000000000

然後可以發現每個人的決策可以被描述成一條折線,像這樣:

000
0001
001100000
111000000000
100000000000

這裡令當前所在座標為邊界,那麼向右走一步代表從每一堆取一顆,向上走一步代表取走最大的一堆。
那麼考慮輸贏情況,首先與邊界相鄰的顯然必敗。
由於只能往上和右,所以上和右均為必輸態的為必勝態。
那麼打表:

111
0101
101011111
010101010111
101010101101

這裡令1為必敗,0為必勝。
那麼可以發現,離原點最近的一個,橫座標等於縱座標的,不緊貼邊界的,座標值最大的點與起點的狀態相同。

那麼O(n)掃一遍就可以過了~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

const int N=100009;
int n,a[N],ans;

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])
        {
            for(int j=i+1;a[j]==i;j++)
                ans^=1;
            ans|=(a[i]-i)&1;
            puts(ans? "First":"Second");
            return 0;
        }

    return 0;
}