传送门
解题思路
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 }