一看就是树形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]} + a[i]
第二种转移需要点技巧才能达到O(n),具体看代码。
初值就是当 i 为叶子的时候,dp[i][1] = INF.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }