【洛谷】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;
}