1. 程式人生 > >[SDOI2006]保安站崗

[SDOI2006]保安站崗

嘟嘟嘟

 

一看就是樹形dp。

剛開始我的狀態dp[i][0 / 1]表示以 i 為根的子樹,i 選 / 不選時的最小的經費。然後轉移方程我就推不出來了,因為無法很好的表示 i 和子樹的關係。比如如果 i 不選,那麼 i 的子節點可以選一個也可以選多個也可以一個不選,因為 i 的兒子的兒子選了的話,i 的兒子就可以不選了。所以只用選和不選表示狀態遠遠不夠。

根據上文的思路,先想一想會出現那些情況:

1. i 選了,那麼 i 的兒子們就隨便。

2. i 沒選,但是 i 的兒子中有的選了,也就是說 i 也被控制了。

3. i 沒被控制。

那麼狀態就是dp[i][0 /1 /2]分別表示:

0:i 沒被控制

1:i 被他爹或某個兒砸控制了

2.自己被選了

考慮轉移,因為是自底向上dp的,所以只用考慮 i 和兒子 j 的關係:

1.dp[i][0]:那麼只有dp[j][1]可以轉移,因為如果從dp[j][0]轉移的話,那麼 j 就永遠無法控制了,不合法;從dp[j][2]轉移,那麼 i 一定被控制,不符合dp[i][0]。所以dp[i][0] = Σdp[j][1]

2.dp[i][1]:則 i 至少要選一個兒子,其他兒子隨便(但不能是dp[j][0],原因和上面一樣),所以dp[i][1] = Σmin{dp[j][1], dp[j][2]} + dp[x][2] (x != j)

2.dp[i][2]:那麼兒子們就隨便了,dp[i][2] = Σmin{dp[j][0], dp[j][1], dp[j][2]}.

第二種轉移需要點技巧才能達到O(n),具體看程式碼。

初值就是當 i 為葉子的時候,dp[i][1] = INF.

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<cstdlib>
 7
#include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const int maxn = 1505; 21 inline ll read() 22 { 23 ll ans = 0; 24 char ch = getchar(), last = ' '; 25 while(!isdigit(ch)) last = ch, ch = getchar(); 26 while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar(); 27 if(last == '-') ans = -ans; 28 return ans; 29 } 30 inline void write(ll x) 31 { 32 if(x < 0) x = -x, putchar('-'); 33 if(x >= 10) write(x / 10); 34 putchar(x % 10 + '0'); 35 } 36 37 int n, a[maxn]; 38 ll dp[maxn][3]; 39 struct Edge 40 { 41 int nxt, to; 42 }e[maxn << 1]; 43 int head[maxn], ecnt = -1; 44 void addEdge(int x, int y) 45 { 46 e[++ecnt] = (Edge){head[x], y}; 47 head[x] = ecnt; 48 } 49 50 void dfs(int now, int f) 51 { 52 ll sum = 0; 53 for(int i = head[now]; i != -1; i = e[i].nxt) 54 { 55 int v = e[i].to; 56 if(v == f) continue; 57 dfs(v, now); 58 dp[now][0] += dp[v][1]; 59 sum += min(dp[v][1], dp[v][2]); 60 dp[now][2] += min(dp[v][0], min(dp[v][1], dp[v][2])); 61 } 62 dp[now][2] += a[now]; dp[now][1] = INF; 63 for(int i = head[now]; i != -1; i = e[i].nxt) 64 { 65 int v = e[i].to; 66 if(v == f) continue; 67 dp[now][1] = min(dp[now][1], sum - min(dp[v][1], dp[v][2]) + dp[v][2]); 68 } 69 } 70 71 int main() 72 { 73 Mem(head, -1); 74 n = read(); 75 for(int i = 1; i <= n; ++i) 76 { 77 int x = read(); a[x] = read(); 78 int m = read(); 79 for(int j = 1; j <= m; ++j) 80 { 81 int y = read(); 82 addEdge(x, y); addEdge(y, x); 83 } 84 } 85 dfs(1, 0); 86 write(min(dp[1][1], dp[1][2])), enter; 87 return 0; 88 }
View Code