[BZOJ3895] 取石子(思維好題 博弈論+找規律)

題目描述

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

輸入
第一行輸入T,表示數據組數。
對於每組測試數據,第一行讀入N。
接下來N個正整數a1,a2…an,表示每堆石子的數量。
輸出
對於每組測試數據,輸出一行。
輸出YES表示Alice有必勝策略,輸出NO表示Alice沒有必勝策略。
樣例輸入
3
3
1 1 2
2
3 4
3
2 3 5
樣例輸出
YES
NO
NO
數據範圍與時空限制
T<=100,n<=50,ai<=1000;
時間限制: 1 Sec 內存限制: 128 MB
思路:
枚舉前幾個樣例,推出規律

原來不想寫思路了,可是發現網上很多題解都用到記憶化搜索,但是這樣不太好寫而且不可以處理較大數據,我在訓練時想到了一個簡單點的規律,下面介紹一下:
1 處理n=1,n=2,n=3的情況,手動模擬即可求出結果,然後下文處理情況就都是n>=4的情況了。
2 當 a[i] 全是1時,如果n是3的倍數,則必輸,否則必勝。
證明:
n=1或n=2可以直接想到取法,n=3時就會發現無論自己怎麼操作,對方都可以進行一次操作使場上出現只有一堆石子而且石子數爲2的情況,這種情況是必輸態,n=4時,可以拿一個石子讓對方面對n=3而且每堆石子數都爲一的情況,所以n=4必勝,n=5時會發現如果A先拿掉一個石子,然後B再拿掉一個石子的話,A就必輸,所以每個人都儘量先不拿石子,然後5堆石子在合併4次會變成1堆石子,而且石子數爲5,此時a先手,就又發現a獲得勝利,n=6時,a先拿一堆石子的話,就會剩下5堆石子的必勝態,a先合併一堆石子,b就拿掉一個石子數爲1的石子,a就會碰到在5堆石子中出現的一個必敗態,所以a在n=6時必敗,此後類比可以退出來。
3 當a[i]不全是1時
首先考慮沒有任何一個1的情況下,會發現石子總數的和對2取餘如果==堆數對2取餘的值,則必勝,否則必輸。
證明
n=1時, 可直接觀察出 石子數爲奇數則先手勝,否則則後手勝,
n=2 時,如果石子數總和爲偶數,先手方則可直接合並兩堆石子,獲得勝利,否則就必敗,因爲此時先手方無論怎樣操作都無法改變後手方經過一次操作就場上出現石子堆數爲一而且石子數總和爲偶數的情況。
n=3時也是同樣道理,因爲此時和爲奇數,先手方就可以通過合併兩堆石子使場上出現n=2時的必敗態,和爲偶數時也是先手方無論怎樣操作都無法阻止後手方通過一次操作使a面臨必敗態的情況。
n>=4時也可以歸納證明。
當a[i]不全是1是,設s爲1的數量,可以發現
當s爲偶數時,後手方可以針對先手方的任何一種操作從而達到取消先手方對一進行操作的影響。
比如,先手方拿走一個1,然後後手方也拿走一個1
或者,先手方把兩個1合併,後手方就把剛剛合併得到的2加到一個非一的數上,這樣既沒改變非一的石子堆數,也沒改變非一的石子數綜合的奇偶性。
再或者,先手方把一個1和一個非一的石子堆合併,後手方就把另一個1加到剛剛合併的那堆石子上,來來恢復非一石子數總和的奇偶性。
所以可以讓s對2取餘,然後就到了代碼的最後一個if else了,這裏的推論已經基本完成了,如果還有問題則在下面評論或者私我qq1807458974;

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    ll n,x,s=0,sum=0,t;
    cin>>t;
    while(t--){
        cin>>n;
        s=0;sum=0;
        for(int i=1;i<=n;i++)
        {
            cin>>x;
            if(x==1)
                s++;
            sum+=x;
        }
        if(s==n)
        {
            if(sum%3!=0)
                cout<<"YES"<<endl;
            else
                cout<<"NO"<<endl;
            continue;
        }
        if(n==1)
        {
            if(sum%2==0)
                cout<<"NO"<<endl;
            else
                cout<<"YES"<<endl;
            continue;
        }
        if(n==2)
        {
            if(s==0&&sum%2==1)
                cout<<"NO"<<endl;
            else
                cout<<"YES"<<endl;
            continue;
        }
        if(n==3)
        {
            if(s==1)
                cout<<"YES"<<endl;
            if(s==2)
            {
                if(sum%2==0&&sum!=4)
                    cout<<"NO"<<endl;
                else
                    cout<<"YES"<<endl;
            }
            if(s==0)
            {
                if(sum%2==n%2)
                    cout<<"YES"<<endl;
                else
                    cout<<"NO"<<endl;
            }
            continue;
        }
        if(s%2==1)
        {
            sum-=s;
            n-=s;
            if(sum==2)
                cout<<"NO"<<endl;
            else
                cout<<"YES"<<endl;
            continue;
        }
        else
        {
            sum-=s;
            n-=s;
            //cout<<sum<<endl;
            if(sum==2||sum%2==n%2)
                cout<<"YES"<<endl;
            else
                cout<<"NO"<<endl;
        }
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章