洛谷P2279 消防局的設立【樹形dp】

題目https://www.luogu.org/problemnew/show/P2279ios

題意:一棵樹。在節點處建消防站,能夠覆蓋與他距離在2以內的節點。問最少要建多少個消防站,能夠覆蓋全部的節點。spa

思路:有一種貪心的思路,看大部分題解都是這樣。code

若是要覆蓋當前節點(本身不建),那麼多是父親,兄弟,祖父建了。blog

可是咱們發現,在祖父建覆蓋的範圍比父親兄弟要更廣一些。因此就貪心的取深度最深的節點,在他的祖父處建一個。get

由於想練dp因此沒寫貪心的。string

看結構感受是樹形dp。$dp[i]$表示以$i$爲根的子樹的狀況,想再開一維表示$i$有沒有建。後來發現狀態好像並不夠。it

由於只考慮子樹的話,當前節點$i$不被覆蓋也不要緊,他能夠被他的父親或祖先覆蓋。io

因此大狀況分紅兩種,$i$被覆蓋和$i$沒被覆蓋。class

其中$i$被覆蓋能夠是由於$i$本身建了,也能夠是由於有一個兒子建了或者是有一個孫子建了。因此這裏有三種狀態。stream

$i$沒被覆蓋還能夠分紅只有$i$沒被覆蓋和$i$和兒子都沒有被覆蓋。這裏又是兩種狀態。

因此總共是5中狀態:

$dp[i][0],在i處建$

$dp[i][1], i處不建但i至少有一個兒子建了$

$dp[i][2],i和兒子都不建但至少有一個孫子建了$

$dp[i][3],本身還沒被覆蓋,兒子已經被覆蓋$

$dp[i][4], 本身和兒子都還沒被覆蓋$

轉移方程:

$dp[i][0] = 1 + \sum min(dp[son][0...4])$每個兒子的任何一種狀態均可以。因此每一個兒子都取5種狀態的最小的。

$dp[i][1] = min(dp[son1][0] + \sum_(son != son1) min(dp[son][0...3]))$,這裏一個巧妙的處理方法是先將每個兒子的$min(dp[son][0...3])$加上,在找到最小的$dp[son][0]-min(dp[son][0...3])$最後加上。

$dp[i][2] = min(dp[son1][1] + \sum_(son!=son1)(min(dp[son][0...2]))$,此時若是son不在子樹被覆蓋的話,別的節點也reach不到了。處理方法和上面也同樣。

$dp[i][3] = \sum min(dp[son][0...2])$

$dp[i][4] = \sum min(dp[son][0...3]$

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<map>
 4 #include<set>
 5 #include<cstring>
 6 #include<algorithm>
 7 #include<vector>
 8 #include<cmath> 
 9 #include<stack>
10 #include<queue>
11 #include<iostream>
12 
13 #define inf 0x3f3f3f3f
14 using namespace std;
15 typedef long long LL;
16 typedef pair<int, int> pr;
17 
18 int n;
19 const int maxn = 1005;
20 int fa[maxn];
21 vector<int>son[maxn]; 
22 int dp[maxn][6];
23 
24 void dfs(int rt)
25 {
26     if(son[rt].size() == 0){
27         dp[rt][0] = 1;
28         dp[rt][1] = dp[rt][2] = inf;
29         dp[rt][3] = dp[rt][4] = 0;
30         return;
31     }
32     dp[rt][0] = 1;
33     int maxson = inf, maxgs = inf;
34     for(int i = 0; i < son[rt].size(); i++){
35         dfs(son[rt][i]);
36         int tmp1 = inf, tmp2 = inf, tmp3 = inf;
37         for(int j = 0; j < 5; j++){
38             tmp1 = min(tmp1, dp[son[rt][i]][j]);
39             if(j < 4)tmp2 = min(tmp2, dp[son[rt][i]][j]);
40             if(j < 3)tmp3 = min(tmp3, dp[son[rt][i]][j]);
41         }
42         dp[rt][0] += tmp1; 
43         dp[rt][1] += tmp2;
44         maxson = min(maxson, dp[son[rt][i]][0] - tmp2);
45         maxgs = min(maxgs, dp[son[rt][i]][1] - tmp3);
46         dp[rt][2] += tmp3;
47         dp[rt][3] += tmp3;
48         dp[rt][4] += tmp2;
49     }
50     dp[rt][1] += maxson;
51     dp[rt][2] += maxgs; 
52     
53 }
54 
55 int main()
56 {
57     scanf("%d", &n);
58     for(int i = 2; i <= n; i++){
59         scanf("%d", &fa[i]);
60         son[fa[i]].push_back(i); 
61     }
62     dfs(1);
63     printf("%d\n", min(dp[1][0], min(dp[1][2], dp[1][1])));
64     
65 }
相關文章
相關標籤/搜索