Codeforces Round #551 (Div. 2) D. Serval and Rooted Tree (树形dp)
https://codeforces.com/contest/1153/problem/D
思路: 用dp[i]表示,结点i最少要有dp[i]个最大的结点肯定被忽略掉(因为题目要求最大化,因此我们希望被忽略掉的最大点尽可能少,所以dp表示的是“最少”的个数)。那么对于整个树,根节点的dp[1]即为整颗树最少要丢弃的结点个数,因此最终答案为k-dp[1]+1(因为我们要选中一个答案,所以少丢弃一个,加1)。
然后初始状态设叶子节点为1,即对于单个点的树来说,自身肯定要被选中。 然后在树上做如下状态转移:如果节点v是max点,那么我们肯定希望从节点v的子节点中选出忽略掉的点最少的那颗子树(忽略掉的点越少,留下的点的最大值就越大),因此d[v]=min(d[child(v)]) ; 如果节点v是min点,那么所有子节点忽略掉的点都要被忽略,只保留一个最小的(保留的这一个蕴含在k-dp[v]+1的“+1”中),因此d[v]=sum(d[child(v)])。
很难表达清楚,也可能自己理解还存在问题,希望能够向大家提供点思路启发。。
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x7fffffff;
const int maxn = 3e5 + 7;
int type[maxn];
vector<int> G[maxn];
int n, k;
int DFS(int u){
if(G[u].size()==0){
k++;
return 1;
}
if(type[u]){
int minn = INF;
for(int i=0;i<G[u].size();i++)
minn = min(minn, DFS(G[u][i]));
return minn;
}else{
int sum = 0;
for(int i=0;i<G[u].size();i++)
sum += DFS(G[u][i]);
return sum;
}
}
int main() {
while(cin>>n){
for(int i=1;i<=n;i++){
scanf("%d",&type[i]);
G[i].clear();
}
for(int i=2;i<=n;i++){
int u;
scanf("%d",&u);
G[u].push_back(i);
}
k=0;
int un = DFS(1);
cout<<k-un+1<<endl;
}
return 0;
}
https://www.codetd.com/article/5883307 这个思路应该更清晰点