HNOI 2003 消防局的建立

2021-07-10 16:14:18 字數 2818 閱讀 5025

2023年,人類在火星上建立了乙個龐大的基地群,總共有n個基地。起初為了節約材料,人類只修建了n-1條道路來連線這些基地,並且每兩個基地都能夠通過道路到達,所以所有的基地形成了乙個巨大的樹狀結構。如果基地a到基地b至少要經過d條道路的話,我們稱基地a到基地b的距離為d。

由於火星上非常乾燥,經常引發火災,人類決定在火星上修建若干個消防局。消防局只能修建在基地裡,每個消防局有能力撲滅與它距離不超過2的基地的火災。

你的任務是計算至少要修建多少個消防局才能夠確保火星上所有的基地在發生火災時,消防隊有能力及時撲滅火災。

輸入格式:

輸入檔名為input.txt。

輸入檔案的第一行為n (n<=1000),表示火星上基地的數目。接下來的n-1行每行有乙個正整數,其中檔案第i行的正整數為a[i],表示從編號為i的基地到編號為a[i]的基地之間有一條道路,為了更加簡潔的描述樹狀結構的基地群,有a[i]< i。

輸出格式:

輸出檔名為output.txt

輸出檔案僅有乙個正整數,表示至少要設立多少個消防局才有能力及時撲滅任何基地發生的火災。

輸入輸出樣例

輸入樣例#1:

6 1

2 3

4 5

輸出樣例#1:

2這是一道蛋疼的樹形dp,樹形dp一般狀態表示比較複雜;在題解幫助下過掉;

%大神crazyxx

狀態:

dp[i][0]:選自己

dp[i][1]:選了至少乙個兒子

dp[i][2]:選了至少乙個孫子

———————————–這三種是覆蓋了自己的

dp[i][3]: 兒子孫子全部覆蓋

dp[i][4]:孫子全部覆蓋

———————————–這兩種並沒有覆蓋自己

建議畫一棵深度為3的完全二叉樹直觀觀察狀態 否則轉移方程比較難懂

初始轉移方程:

dp[i][0] = 1+σmin(dp[j][0…4]);

要使選了根節點之後合法(整棵子樹包括根節點被覆蓋)必須使兒子的孫子全部覆蓋 0~4狀態滿足

dp[i][1] = min( dp[k][0] + σ(j != k)min(dp[j][0…3]) );

要使選了乙個兒子之後合法 由於兒子只可以覆蓋到兄弟 所以孫子一定要全部被覆蓋 即兒子的兒子一定覆蓋 0~3滿足

dp[i][2] = min( dp[k][1] + σ(j != k)min(dp[j][0…2]) );

使選了乙個孫子之後合法 由於孫子最多只能覆蓋到當前節點 所以兒子一定全部覆蓋 即所有兒子本身要被覆蓋 0~2滿足

dp[i][3] = σdp[j][0…2];

要使兒子及孫子全部被覆蓋 即兒子本身要被覆蓋 0~2滿足

dp[i][4] = σdp[j][0…3];

要使孫子全部被覆蓋 即兒子的兒子要全部被覆蓋 0~3滿足

::注意每種狀態由兒子轉移過來所以根的情況 要轉化成對於兒子來說的情況

然後改進狀態 因為每種轉移方程至少有三種可能最後取其中較小的 故時間效率較低 令dp[i][k]表示min(dp[i][0],dp[i][1]….dp[i][k])且k>=2 因為上述轉移方程最少都是0~2狀態

那麼轉移方程就大幅度化簡了:

dp[i][0] = 1+σdp[j][4];

直接由上面變形而來

dp[i][1] = dp[i][4] + min(dp[k][0]-dp[k][3]);

選乙個兒子 需保證所有孫子被覆蓋 即 dp[i][4] 然後要選出乙個兒子 將他從0~3狀態變為選了自己(由於dp[i][4]中他是3狀態所以要減去乙個dp[k][3]) 取這個差值最小的兒子

dp[i][2] = dp[i][3] + min(dp[k][1]-dp[k][2]);

選乙個孫子 與上面類似 要保證所有兒子都被覆蓋 即dp[i][3] 再將乙個兒子從0~2狀態變為0~1狀態以保證覆蓋他父節點

dp[i][3] = σdp[j][2];

保證所有兒子被覆蓋 兒子的0~2狀態均符合條件

dp[i][4] = σdp[j][3];

保證所有兒子的兒子被覆蓋 兒子的0~3狀態均符合條件

別問我為什麼dp[i][1]和dp[1][2]用到後面的狀態 因為你只需要在過程中記下那一坨min的值 把3,4處理完後再算1,2

另外由於資料特殊性 編號大的節點一定是編號小的節點的後代 所以遞推順序直接到著推就好了

**:(神犇的**稍作改動)

#include

#include

#include

using

namespace

std;

const

int maxn=1000+10;

const

int inf=(1

<<30);

bool g[maxn][maxn];

int dp[maxn][5];

int main()

for(int i = n ; i>=1 ; i--)

dp[ i ][ 1 ]=dp[ i ][ 4 ]+x1;

dp[ i ][ 2 ]=min(dp[ i ][ 3 ]+x2,min(dp[ i ][ 0 ],dp[ i ][ 1 ]));

dp[ i ][ 3 ]=min(dp[ i ][ 2 ],dp[ i ][ 3 ]);

dp[ i ][ 4 ]=min(dp[ i ][ 3 ],dp[ i ][ 4 ]);

}printf("%d",dp[ 1 ][ 2 ]);

return

0;}

覺得做完之後對於樹形dp有了新的認識 = =漲姿勢了

HNOI2003 消防局的設立

2020年,人類在火星上建立了乙個龐大的基地群,總共有n個基地。起初為了節約材料,人類只修建了n 1條道路來連線這些基地,並且每兩個基地都能夠通過道路到達,所以所有的基地形成了乙個巨大的樹狀結構。如果基地a到基地b至少要經過d條道路的話,我們稱基地a到基地b的距離為d。由於火星上非常乾燥,經常引發火...

HNOI2003 消防局的設立

2020年,人類在火星上建立了乙個龐大的基地群,總共有n個基地。起初為了節約材料,人類只修建了n 1條道路來連線這些基地,並且每兩個基地都能夠通過道路到達,所以所有的基地形成了乙個巨大的樹狀結構。如果基地a到基地b至少要經過d條道路的話,我們稱基地a到基地b的距離為d。由於火星上非常乾燥,經常引發火...

HNOI2003 消防局的設立

傳送門 這道題似乎是夾克老爺的憤怒那道題的弱化版 這次的距離是固定為2的。我們首先考慮一下只有一條鏈的情況,這個誰都會,就是每隔2k k為給定距離 個點放乙個,就是這樣貪心。樹也可以用這種貪心法來求解,我們從葉子節點往上dp,每次用dp i 表示這個點還能往上控制距離為多少的點,如果當前的dp值為 ...