「貪心」做題記錄

DengStar發表於2024-11-12

「貪心」做題記錄

  1. P2672 [NOIP2015 普及組] 推銷員

    由於不會走多餘的路,所以行走產生的疲勞值只和最遠的被推銷的住戶有關。設 \(f_X(i)\) 表示總共選 \(X\) 家住戶,且第 \(i\) 戶是最遠的被推銷的住戶的情況下,最大的疲勞值。顯然可以貪心地在前 \(i-1\) 戶中選擇 \(X-1\) 戶疲勞值最大的住戶,所以 \(f_X(i) = 2S_i + A_i + 前(i-1)個A_j中最大的(X-1)個A_j的和\)

    可以用小根堆(優先佇列)維護前 \(k\) 大和。由於要列舉 \(X\),總的時間複雜度為 \(O(N^2\log N)\),可以得到 60 分。程式碼

    下面是本題的正解貪心做法。

    考慮貪心地選取前 \(X\) 個疲勞值最大的住戶。但這並不一定是最優的:我們可能選取一個疲勞值更小,但比原來所有住戶都更遠的住戶,從而透過行走距離獲得更大的疲勞值。

    具體地,我們要放棄原先的多少個住戶呢?實際上最多放棄 1 個,然後選擇未被選擇的住戶中,\(2S_i+A_i\) 最大的住戶 \(i\)。如果放棄多於 1 個住戶,一定不優於只放棄 1 個。(不知道怎麼表達證明過程)

    AC 程式碼

  2. [ABC137D] Summer Vacation

    如果一個工作在某一天做完之後,直到結束都不能領到工資,那麼還不如不做。所以可以把一個工作可以做的區間看作一段字首。從後往前列舉天數,維護可以做的工作的集合,每次選擇集合中價值最大的工作來做。(不會嚴謹證明,但字首的觀點或許有助於理解。)

    提交記錄

  3. [AGC004D] Teleporter

    洛谷的題解都他媽是什麼東西?怎麼都這麼意識流,還有的是如寫

    首先根據題目中圖的性質,可以得出在忽略 \(1\) 出發的邊以後,圖一定是一棵以 \(1\) 為根的內向樹。

    證明:刪去 \(1\) 出發的邊顯然不影響其它點到 \(1\) 的可達性,這時邊數 \(=\) 點數 \(-1\)。根據連通性,可以得知圖是樹。再根據除了 \(1\) 之外的點都可達 \(1\),可以得知圖是以 \(1\) 為根的內向樹。

    從樹的角度來看,可以得出 \(1\) 必須指向自己。因為在樹上兩點的路徑唯一,而且是內向樹,如果一個點的深度小於 \(K\),並且沒有這個自環,它就無法透過恰好 \(K\) 次傳送到達 \(1\)

    如果一個點的深度大於 \(K\),那麼它顯然無法經過恰好 \(K\) 次傳送到達根節點;否則一定可以,缺少的部分可以透過一直走 \(1\) 的自環來補足。那麼我們把問題轉化成了:給定一棵樹,修改最少的邊,使得所有點的深度不大於 \(K\)

    這個問題還可以進一步轉化:刪除最少的邊,使得剩下的所有弱連通塊(每個弱連通塊都是樹)都滿足以下條件:

    • 如果根節點為 \(1\),那麼樹的高度不超過 \(K\)
    • 否則,樹的高度不超過 \(K - 1\)

    採用 dfs ,自下往上解決此問題。如果一棵子樹的高度大於等於 \(K - 1\),並且它的父節點不是 \(1\),那麼需要刪除它連向父節點的邊,否則更新父節點的高度。

    function<void(int)> dfs = [&](int u)
    {
        h[u] = 0;
        for(int v: G[u])
        {
            dfs(v);
            if(u != 1 && h[v] == K - 1)
                ans++;
            else
                h[u] = max(h[u], h[v] + 1);
        }
    };
    dfs(1);
    

    這樣得出來的圖一定滿足條件,但刪邊數的最少性不會證明。官方題解用的就是這個做法。

    完整程式碼