题目传送门
解题思路:
树形DP
可知一个点被控制有且仅有一下三种情况:
1、被父亲节点上的保安控制
2、被儿子节点上的保安控制
3、被当前节点上的保安控制
我们设dp[0/1/2][u]表示u节点所在子树中全部被控制的最小代价,0表示只有u节点尚未被控制(等待被其父亲节点控制);
1表示u节点已经被控制,但u节点上没有保安,所以不能去控制其父亲节点;2表示u节点上有保安
(机房的神犇说多维数组要把小的那一维写在前面,因为可以优化常数,原理请自行翻阅一本通)
转移:(以下设v是u的儿子节点)
dp[0][u]=∑min(dp[1][v],dp[2][v]) i节点上反正没有保安,那么儿子节点只要保证全部控制即可,显然1,2状态都是满足的
dp[1][u]=∑min(dp[1][v],dp[2][v]) + 某一个dp[2][v] 也就是说对于其中一个儿子取dp[2][v]而其他儿子取min(dp[1][v],dp[2][v])意为i号点必须要找一个儿子来覆盖它,其余随意。这个地方涉及到了算法复杂度的问题,楼下有些题解在这里写的是O(n^2)的转移,但实际上完全可以做到O(n)。具体在代码中细讲。
dp[2][u]=∑min(dp[0][v],dp[1][v],dp[2][v])+val[u] 这个就简单了,i号点上反正有保安了,所有儿子节点都无所谓了,全部可以转移。
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #include<cstring> 5 6 using namespace std; 7 8 int n,a[1502],k,m,f[1502][3]; 9 vector<int> l[1502]; 10 11 inline void dfs(int root,int fa) { 12 bool flag = 0; 13 int sum = 0,_min = 0x3f3f3f3f,id = 0; 14 f[root][2] = a[root]; 15 f[root][1] = f[root][0] = 0; 16 for(int i = 0;i < l[root].size(); i++) { 17 if(fa == l[root][i]) continue; 18 dfs(l[root][i],root); 19 f[root][0] += min(f[l[root][i]][1],f[l[root][i]][2]);//被父亲保 20 f[root][1] += min(f[l[root][i]][1],f[l[root][i]][2]);//被儿子保 21 if(f[l[root][i]][1] > f[l[root][i]][2]) flag = 1; 22 else _min = min(_min,f[l[root][i]][2] - f[l[root][i]][1]); 23 f[root][2] += min(f[l[root][i]][1],min(f[l[root][i]][2],f[l[root][i]][0]));//自保 24 } 25 if(!flag) 26 f[root][1] += _min; 27 } 28 29 int main() { 30 scanf("%d",&n); 31 for(int i = 1;i <= n; i++) { 32 scanf("%d%d%d",&k,&a[i],&m); 33 for(int j = 1;j <= m; j++) { 34 int x; 35 scanf("%d",&x); 36 l[k].push_back(x); 37 l[x].push_back(k); 38 } 39 } 40 memset(f,0,sizeof(f)); 41 dfs(1,-1); 42 printf("%d",min(f[1][1],f[1][2])); 43 return 0; 44 }