原創 | 你追我,若是你追到我……那就算你贏了



adebdae03886f42d9e41596fcee0c550.jpeg

你們好,歡迎閱讀週末算法題專題。算法

今天選擇的算法題來源於昨天同一套題中的D題,這題全場經過的人數在2600人左右。雖然經過的人數更少了一些,可是題目的難度卻並無增長不少,可是趣味度增長了。我也是第一次碰見這樣的問題。數組

題目連接:https://codeforces.com/contest/1405/problem/D數據結構

廢話很少說,咱們一塊兒來看這題的題意。app

題意


咱們都知道數據結構當中的樹有這樣一個性質,若是樹當中有n個點,那麼它應該由n-1條無向邊組成。而且樹當中是必定沒有環的,若是有環的話n-1條邊就不夠了。ide

今天是說有兩我的分別叫作Alice和Bob在一棵樹上玩遊戲,這兩我的名是業內的慣例。凡是兩我的玩遊戲的題目,主人公的名字不少都叫Alice和Bob,我也不知道這個慣例的由來,你們知道這麼回事就行了。Alice和Bob兩人各自佔據了樹上的一個點,而後兩我的交替移動。Alice先手,Bob後手。測試

Alice每一次移動最多能夠移動da距離,Bob最多能夠移動db距離,兩我的也能夠放棄移動。假設兩人都絕頂聰明每一次都會選擇最佳策略,請問通過最多個回合以後,Alice可否捉到Bob呢?若是能夠則Alice獲勝,不然Bob獲勝。注意在Bob的回合,它能夠通過Alice的節點,這並不會被視爲捉到。ui

不知道爲何看到這道題的時候,老是會想起費玉清的段子,不知道大家有沒有這種感受。url

樣例


第一行輸入一個數t,表示測試數據的組數。spa

對於每組數據首先有5個數,分別是n, a, b, da, db。n表示樹節點的個數,a和b表示遊戲開始時a和b出生點的位置。da和db表示Alice和Bob每回合最多移動的距離。這幾個數的最大範圍都是,而且多組數據當中全部的n相加不超過code

咱們來看兩組樣例:

4 3 2 1 2
1 2
1 3
1 4
6 6 1 2 5
1 2
6 5
2 3
3 4
4 5

第一組數據是這樣的:

藍色表示Alice,紅色是Bob。因爲Alice先手,只要Alice移動到1節點,不管Bob如何移動,他都必輸無疑。

第二組數據:

因爲Alice最多隻能移動兩格,第一回合移動到3,Bob選擇不動。只要Alice移動,不管是一格仍是兩格,Bob均可以直接移動到1節點。也就是說最終Bob就在1和6節點之間來回移動,躲開Alice的追捕。

題解


看到Alice和Bob兩人遊戲,而且兩人都絕頂聰明會選擇最佳策略,首先想到的就是博弈論。可是觀察一下你會發現這題和博弈論沒有什麼關係,由於博弈論每每都是公平博弈,局面會有狀態轉移。但這題當中不是,這題不存在勝負狀態轉移,必定會有一個肯定的結果,就是是誰勝誰負,因此這題不知足博弈論的前提。

相似博弈論的題面只是障眼法而已,若是你信了,而且真的往這方面努力,那麼你就被出題人騙了。不過這個陷阱仍是比較明顯的,很容易看出來。接下來咱們又遇到了另一個陷阱,這個陷阱也很明顯,就是執行的回合數量。題目給的是,這個數是真正的天文數字,比宇宙當中全部的粒子數都要多。因此咱們能夠徹底能夠理解成無限遊戲。

若是出題人陰險一點,徹底能夠給一個這個量級的回合數,會更有欺騙性一些。其實咱們想一下就會發現,樹上節點最多隻有個,當它們玩追逐遊戲的時候,只會有兩種狀況,一種是永遠也追不上,還有一種是很快就追上。因此這個回合數沒什麼意義,就是唬人的。

洞見


首先,第一個洞見是這道題咱們使用模擬是不可行的。所謂的模擬也就是模擬題意的運行狀況,去一步一步地分析每個玩家的選擇,作出最好的決策,最後得出遊戲的結果。不可行的緣由也很簡單,由於會超時。這個很是明顯,就不深刻解釋了。

除此以外,咱們還能夠獲得其餘一些洞見。首先第一個很簡單的洞見是,若是Alice和Bob出生的位置相距小於da的話,那麼Alice必勝。這個也很好理解,一開始的距離就在Alice的移動範圍內,那Bob一上來就被抓住了。沒有討論的餘地。另一個洞見是若是da >= db,那麼Alice必勝。

也就是若是Bob跑得比Alice慢的話,他也同樣必敗。這也很簡單,由於地圖的範圍是有限的,一個追一個逃,逃的速度比追的慢,顯然他逃不出去。這個結論雖然簡單,可是反過來一想會獲得一個問題。若是Bob跑得比Alice快的話,他必定能夠獲勝嗎

咱們看第一個案例就知道這個答案不必定,由於地形也會影響最終的結果。樹意味着每個節點都全連通,也意味着每兩點的路線只有一條。換句話說每一條路線都是思路,即便Bob跑得快若是不反向跑甩掉Alice的話,那麼他也是同樣會被Alice追上的。

因此咱們接下來要作的就是深刻分析這裏的狀況,把剩下的問題捋清楚。

回味樣例


codeforces的問題有一個特色就是咱們必定要深刻分析樣例,由於不少出題人很良心,給出的樣例都是關鍵樣例。咱們能夠藉助樣例的狀況幫助咱們分析問題。好比今天說的這題就是一個。

咱們來分析一下第一個樣例:

圖片.png

這就是典型的Bob跑得比Alice快的樣例,Bob不只跑得比Alice快,甚至仍是Alice的兩倍。可是咱們都知道結果仍是Alice獲勝,緣由也很簡單,Alice移動到1號節點以後,Bob只有兩個選擇要麼1號節點要麼4號節點,這兩個節點都距離1號節點只有一個距離,仍然在Alice追上的範圍內。

在這個樣例當中Bob的逃跑空間勉強是夠的,但仍是被追上了,說明他的逃跑速度是不夠的。不夠的緣由也很簡單,由於一開始當Alice移動到1節點以後,他距離Bob的距離是1,也就是一個da。這時Bob無路可走只能折返甩開Alice,這時候他最多可以走一個db,他要想不被Alice捉住,首先他須要先走過da這一段距離,接着他須要走出至少da+1的距離,才能保證Alice折返追他也追不到。那麼咱們能夠得出一個條件是db > 2da。

但咱們發現僅僅db > 2da仍是不夠的,由於在這個例子當中咱們能夠看得出來不夠開闊,即便db=3,Bob也沒處可去。也就是說在這棵樹上至少有一條鏈路它的長度要大於2da才行,不然即便Bob再能跑也會被地形限制住。對於一棵樹而言,求它的最長鏈路仍是比較簡單的,咱們也在以前的文章當中講解過,其實就是對於每個節點都求一個到葉子節點的最長距離和次長距離之和。全部的節點的距離最大的那個就是整棵樹上的最長鏈路。

咱們整理一下思路,一共發現了3個條件,只有知足這3個條件,Bob纔可能跑掉,不然必定會被Alice捉住。這三個條件分別是:

  1. 起始的時候Bob和Alice距離大於da
  2. 樹的直徑大於2da
  3. db大於2da

因此咱們要求的就只有兩點,第一點是一開始它們之間的距離,以及樹的直徑(樹上最長的鏈路長度)。好在這兩點均可以經過遞歸實現。都理出來了以後代碼就不難寫了:

t = int(input())

depth = [0 for _ in range(200050)]
for _ in range(t):
    n, a, b, da, db = list(map(int, input().split(' ')))
    edges = [[] for _ in range(n+2)]
    for i in range(n-1):
        u, v = list(map(int, input().split(' ')))
        edges[u].append(v)
        edges[v].append(u)

    diameter = 0

    depth[a] = 0
    def dfs(u, f):
        global diameter
        l = 0
        for v in edges[u]:
            if v == f:
                continue
            # depth數組記錄每一個節點的樹深
            depth[v] = depth[u] + 1
            cur = 1 + dfs(v, u)
            # cur + l即u節點到葉子節點的最長距離和次長距離
            # 直徑就是這二者和之中最大的一個
            diameter = max(diameter, cur + l)
            l = max(l, cur)

        return l

 # 以Alice所在的點做爲樹根,這樣depth[b]即Alice和Bob的距離
    dfs(a, -1)
    if depth[b] <= da or da * 2 >= diameter or da*2 >= db:
        print('Alice')
    else:
        print('Bob')

咱們把整個思路說穿了是否是有一種一文不值的感受?可是本身思考要想明白仍是不太容易的,codeforces的問題就是這樣,常常須要咱們在紙上畫一畫看一看。有時候一些點靠本身想很難徹底想明白,可是找一個例子試一試一下就清楚了。這也是codeforces問題有趣的地方之一。

相關文章
相關標籤/搜索