Codeforces Round #551 (Div. 2) D. Serval and Rooted Tree (树形dp)

https://codeforces.com/contest/1153/problem/D
Codeforces Round #551 (Div. 2) D. Serval and Rooted Tree (树形dp)_第1张图片
思路: 用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
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 这个思路应该更清晰点

你可能感兴趣的:(题解,动态规划)