1. 程式人生 > 實用技巧 >LG P7078 貪吃蛇

LG P7078 貪吃蛇

Description

草原上有 $n$條蛇,編號分別為 $1,2,\ldots,n$。初始時每條蛇有一個體力值$a_i$,我們稱編號為 $x$的蛇實力比編號為 $y$的蛇強當且僅當它們當前的體力值滿足$a_x > a_y$,或者 $a_x=a_y$且 $x > y$

接下來這些蛇將進行決鬥,決鬥將持續若干輪,每一輪實力最強的蛇擁有選擇權,可以選擇吃或者不吃掉實力最弱的蛇:

  1. 如果選擇吃,那麼實力最強的蛇的體力值將減去實力最弱的蛇的體力值,實力最弱的蛇被吃掉,退出接下來的決鬥。之後開始下一輪決鬥。
  2. 如果選擇不吃,決鬥立刻結束。

每條蛇希望在自己不被吃的前提下在決鬥中儘可能多吃別的蛇(顯然,蛇不會選擇吃自己)。

現在假設每條蛇都足夠聰明,請你求出決鬥結束後會剩幾條蛇。

本題有多組資料,對於第一組資料,每條蛇體力會全部由輸入給出,之後的每一組資料,會相對於上一組的資料,修改一部分蛇的體力作為新的輸入。

Solution

結論:當前最強的蛇吃掉最弱的蛇之後如果沒有變成最弱的蛇(情況一),那麼它一定會選擇吃

說明:

假設當前最強的蛇叫張三,並且它吃掉最弱蛇之後不是最弱的,那麼它下一輪必不會被吃

  • 如果張三吃掉最弱的之後仍然是最強的,不吃就顯然很虧,所以張三會選擇吃
  • 如果張三吃掉最弱的之後不是最強的,此時的最強的蛇一定沒有剛才強,最弱的也沒有剛才弱,如果此時最強蛇選擇吃,那麼它的體力值一定比張三小,就算死也會死在張三前面,又因為它足夠聰明,不會使自己死,所以張三也不會死
  • 如果張三吃掉最弱的之後不是最強的,此時的最強的蛇一定沒有剛才強,最弱的也沒有剛才弱,如果此時最強蛇選擇不吃,遊戲結束,張三顯然不死

如果吃了之後變成最弱的蛇(情況二),張三是否死將由此時的最強蛇李四決定,張三非常聰明,仔細一想,如果李四決定吃張三,那麼張三這一次就不會吃蛇使得自己成為最弱蛇,反之,如果李四不想吃,那麼張三就可以放心吃

那麼李四是否吃呢?又可以分成以上兩種情況進行討論,假如李四吃後不是最弱蛇,那麼李四會選擇吃,張三預判到李四的操作,所以選擇不吃來避免死亡

由此會發現進入第二種情況後,吃與不吃交替出現:張三預判李四,李四預判王五,王五預判趙六,……,趙六選擇吃,王五選擇不吃,李四選擇吃,張三選擇不吃

所以此時張三吃或不吃由之後這個“吃不吃”序列的長度的奇偶性決定

並且這個序列長度$\geq 2$,所以序列中必定有一個不吃,所以當遊戲進入情況二後,不會重新回到情況一,而是會再吃$0$或$1$次,然後結束

那麼做法就是模擬兩個階段

用set可以很好的維護最強蛇和最弱蛇,這種做法的時間複雜度$O(Tn\log n)$

正解是用兩個雙端佇列維護蛇的序列,使其的體力值單調,一頭強一頭弱

每次從兩個佇列尾取出最強,從某個佇列中取出最弱,不斷重複,因為新產生的蛇的體力值也具有單調性,所以不需要$\log n$資料結構維護

時間複雜度$O(Tn)$

#include<iostream>
#include<utility>
#include<cstdio>
#include<deque>
using namespace std;
int T,n,a[1000005],ans,cnt;
deque<pair<int,int> >q1,q2;
inline int read()
{
    int f=1,w=0;
    char ch=0;
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        w=(w<<1)+(w<<3)+ch-'0';
        ch=getchar();
    }
    return f*w;
}
int main()
{
    T=read();
    for(int i=1;i<=T;i++)
    {
        q1.clear(),q2.clear(),ans=cnt=0;
        if(i==1)
        {
            n=read();
            for(int j=1;j<=n;j++) a[j]=read();
        }
        else
        {
            int k=read(),x,y;
            for(int j=1;j<=k;j++) x=read(),y=read(),a[x]=y;
        }
        for(int i=1;i<=n;i++) q1.push_back(make_pair(a[i],i));
        while(true)
        {
            if(q1.size()+q2.size()==2)
            {
                ans=1;break;
            }
            int minn=q1.front().first,maxx,id;
            q1.pop_front();
            if(q2.empty()||q1.size()&&q1.back()>q2.back()) maxx=q1.back().first,id=q1.back().second,q1.pop_back();
            else maxx=q2.back().first,id=q2.back().second,q2.pop_back();
            pair<int,int>temp=make_pair(maxx-minn,id);
            if(temp>q1.front()) q2.push_front(temp);
            else
            {
                ans=q1.size()+q2.size()+2;
                while(true)
                {
                    ++cnt;
                    if(q1.size()+q2.size()==1)
                    {
                        if(!(cnt%2)) --ans;
                        break;
                    }
                    int x,ID;
                    if(q2.empty()||q1.size()&&q1.back()>q2.back()) x=q1.back().first,ID=q1.back().second,q1.pop_back();
                    else x=q2.back().first,ID=q2.back().second,q2.pop_back();
                    temp=make_pair(x-temp.first,ID);
                    if(temp<q1.front()&&(q2.empty()||temp<q2.front()));
                    else
                    {
                        if(!(cnt%2)) --ans;
                        break;
                    }
                }
                break;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
貪吃蛇