题目传送门
解题思路:
树形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 }
来源:https://www.cnblogs.com/lipeiyi520/p/12215755.html