Codeforces 682C Alyona and the Tree(树形DP)

题目大概说给一棵点有权、边也有权的树。一个结点v不高兴当且仅当存在一个其子树上的结点u,使得v到u路径上的边权和大于u的权值。现在要不断地删除叶子结点使得所有结点都高兴,问最少删几个叶子结点。

一开始题目看错了,以为说的是v到u路径上的边权和小于v的权值,然后想出了个解法:从根开始DFS,找高兴的结点,递归过程中在set插入各个祖先结权值,递归返回时从set中删除,而如果set里面最小的元素小于当前结点的路径和那么这个结点就不能要直接return,另外还用到一个简单的数学原理——两个数同时加上相同的数其大小关系不变。。时间复杂度O(nlogn)。

然后写好后才发现读错题。而事实上这题反而更容易。。同样也是从根开始DFS,遇到不高兴的就不往下DFS了,而判断是否高兴就用到个简单的DP了:

  • dp[u]表示祖先到u结点中的最大边权和
  • 由于边权可以为负,所以转移就是dp[v]=max(dp[u]+weight(u,v),weight(u,v))
  • 而u结点不高兴,当且仅当d[u]>weight(u)

另外可以不用long long,这个是没问题的。。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 #define MAXN 111111
 6 struct Edge{
 7     int v,w,next;
 8 }edge[MAXN<<1];
 9 int NE,head[MAXN];
10 void addEdge(int u,int v,int w){
11     edge[NE].v=v; edge[NE].w=w; edge[NE].next=head[u];
12     head[u]=NE++;
13 }
14 
15 int val[MAXN],ans;
16 int d[MAXN];
17 void dfs(int u){
18     if(u!=1 && d[u]>val[u]){
19         return;
20     }
21     ++ans;
22     for(int i=head[u]; i!=-1; i=edge[i].next){
23         int v=edge[i].v;
24         d[v]=max(d[u]+edge[i].w,edge[i].w);
25         dfs(v);
26     }
27 }
28 int main(){
29     int n;
30     scanf("%d",&n);
31     for(int i=1; i<=n; ++i){
32         scanf("%d",val+i);
33     }
34     NE=0;
35     memset(head,-1,sizeof(head));
36     int a,b;
37     for(int i=2; i<=n; ++i){
38         scanf("%d%d",&a,&b);
39         addEdge(a,i,b);
40     }
41     dfs(1);
42     printf("%d",n-ans);
43     return 0;
44 }
原文地址:https://www.cnblogs.com/WABoss/p/5662863.html