1. 程式人生 > 其它 >【洛谷】P1288 取數遊戲II(博弈論+思維)

【洛谷】P1288 取數遊戲II(博弈論+思維)

題目描述

有一個取數的遊戲。初始時,給出一個環,環上的每條邊上都有一個非負整數。這些整數中至少有一個0。然後,將一枚硬幣放在環上的一個節點上。兩個玩家就是以這個放硬幣的節點為起點開始這個遊戲,兩人輪流取數,取數的規則如下:

(1)選擇硬幣左邊或者右邊的一條邊,並且邊上的數非0;

(2)將這條邊上的數減至任意一個非負整數(至少要有所減小);

(3)將硬幣移至邊的另一端。

如果輪到一個玩家走,這時硬幣左右兩邊的邊上的數值都是0,那麼這個玩家就輸了。

如下圖,描述的是Alice和Bob兩人的對弈過程,其中黑色節點表示硬幣所在節點。結果圖(d)中,輪到Bob走時,硬幣兩邊的邊上都是0,所以Alcie獲勝。

(a)Alice (b)Bob (c)Alice (d)Bob

現在,你的任務就是根據給出的環、邊上的數值以及起點(硬幣所在位置),判斷先走方是否有必勝的策略。

輸入格式

第一行一個整數N(N≤20),表示環上的節點數。

第二行N個數,數值不超過30,依次表示N條邊上的數值。硬幣的起始位置在第一條邊與最後一條邊之間的節點上。

輸出格式

僅一行。若存在必勝策略,則輸出“YES”,否則輸出“NO”。

輸入輸出樣例

輸入

4
2 5 3 0

輸出

YES

輸入

3
0 0 0

輸出

NO

*******************************************************************************************************************************************************************************

解題思路:

這個題目首先沒有模板可以套用,我們只能只能找規律,如果自底向上尋找規律不太好找,就像這道題目,這樣推情況太多不太好想,我們可以嘗試著從必勝狀態去倒推,就像dp那樣劃分成一個個子問題,我們可以想象到,如果最後不管是先手還是後手獲勝,目的是讓另一方無路可走,又因為一次最多可以創造出一條0邊,那麼意味著獲勝的那一方一定是挨著另一個0邊的,這樣0-n-0的狀態才可以讓自己獲勝

那麼我們推廣一下,我們每次走的時候就是要可能的創造出0邊,因為如果不創造出0邊,只走權值的一點點的話,另一方立馬可以走完剩下的權值讓對手無路可走獲得勝利,所以不管是先手還是後手都會選擇狂奔,權值也就沒有了任何意義

所以我們只需要判斷從起點開始,向左向右找0邊,只要有一個方向存在奇數個點,先手就必勝,否則先手必敗

下面附上ac程式碼

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <queue>
using namespace std;
typedef long long ll;
ll a[1000010];
int main()
{
    std::ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    ll n;
    cin>>n;
    for(ll i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    int flag=0;
    int cnt=0;
    for(ll i=1;i<=n;i++)
    {
        if(a[i]!=0)
            cnt++;
        else
            break;
    }
    if(cnt%2==1)
        flag=1;
    cnt=0;
    for(ll i=n;i>=1;i--)
    {
        if(a[i]!=0)
            cnt++;
        else
            break;
    }
    if(cnt%2==1)
        flag=1;
    if(flag==1)
        cout<<"YES"<<endl;
    else
        cout<<"NO"<<endl;
    return 0;
}