洛谷 P2458 [SDOI2006]保安站岗(树形dp)

传送门


解题思路

dp[i][j]表示以i为根的子树的最小花费。

其中j表示节点i依靠的是哪里放置保安。

  • j==0:依靠自己
  • j==1:依靠儿子
  • j==2:依靠父亲

因为叶子节点不能依靠儿子,所以把所有的叶子节点i的dp[i][1]赋值为最大值。

因为根节点不能依靠父亲,所以最后取答案时为min(dp[root][0],dp[root][1])。

转移方程怎么写?

比较麻烦

  • dp[i][0]:把所有儿子节点的min值加起来最后加上w[i]即可。
  • dp[i][2]:把所有儿子节点的dp[v][0],dp[v][1]取min后加起来即可。
  • dp[i][1]:把所有儿子节点的dp[v][0],dp[[v][1]取min后加起来,然后因为节点i要依靠其中一个儿子,所以当所有儿子都去了dp[v][1]时,dp[i][1]还要加上最小的(dp[v][0]-dp[v][1])。

AC代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,dp[1505][3],k[1505],m[1505],vis[1505]; 
 4 vector<int> d[1505]; 
 5 void dfs(int u){
 6     dp[u][0]=k[u];
 7     int min0=0x3f3f3f3f;
 8     int cnt=0;
 9     for(int i=0;i<d[u].size();i++){
10         int v=d[u][i];
11         dfs(v);
12         dp[u][0]+=min(dp[v][0],min(dp[v][1],dp[v][2]));
13         dp[u][2]+=min(dp[v][0],dp[v][1]);
14         if(dp[v][0]>dp[v][1]){
15             dp[u][1]+=dp[v][1];
16             min0=min(min0,dp[v][0]-dp[v][1]); 
17         }else{
18             dp[u][1]+=dp[v][0];
19             cnt++;
20         }
21     }
22     if(!cnt){
23         dp[u][1]+=min0;
24     }
25 }
26 int main(){
27     cin>>n;
28     for(int j=1;j<=n;j++){
29         int i;
30         cin>>i;
31         scanf("%d%d",&k[i],&m[i]);
32         if(m[i]==0){
33             dp[i][1]=0x3f3f3f3f;
34         }
35         for(int k=1;k<=m[i];k++){
36             int x;
37             scanf("%d",&x);
38             d[i].push_back(x);
39             vis[x]=1;
40         }
41     }
42     int root = 0;
43     for(int i = 1; i <= n; i++)
44         if(!vis[i]) root = i;
45     dfs(root);
46     cout<<min(dp[root][0],dp[root][1]);
47     return 0;
48 }

猜你喜欢

转载自www.cnblogs.com/yinyuqin/p/12219778.html