洛谷 P2458 [SDOI2006]保安站岗

题目传送门

解题思路:

树形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 } 

猜你喜欢

转载自www.cnblogs.com/lipeiyi520/p/12215755.html