codeforces 219D 树形dp

题目链接:http://codeforces.com/problemset/problem/219/D

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<utility>
using namespace std;

const int maxn = 2*1e5+100;

int dp[maxn];  //dp[i]表示以i为根遍历全部的点的最小的权值。

struct Edge{
    int u,v,w;
    int next;
    Edge(int u=0,int v=0,int w=0,int next=0): u(u), v(v), w(w), next(next) {}
}edges[maxn*2];
int head[maxn],cnt;
int sum;  //以1为根的树至少要修改的边数。
int ans;

void addedge(int u,int v){
    edges[cnt] = Edge(u,v,0,head[u]);
    head[u] = cnt++;
    edges[cnt] = Edge(v,u,1,head[v]);
    head[v] = cnt++;
}

void dfs1(int u,int fa){
    for(int i=head[u];i!=-1;i=edges[i].next){
        Edge& e = edges[i];
        if(e.v == fa) continue;
        sum += e.w;
        dfs1(e.v,u);
    }
}

void dfs2(int u,int fa){
    for(int i=head[u];i!=-1;i=edges[i].next){
        Edge& e = edges[i];
        if(e.v == fa) continue;
        if(e.w == 1) dp[e.v] = dp[u] - 1;   //由于父亲节点的dp[u]已经算出,只需要更新就是了。
        else         dp[e.v] = dp[u] + 1;
        ans = min(ans,dp[e.v]);
        dfs2(e.v,u);
    }
}

int main()
{
    //freopen("E:\acm\input.txt","r",stdin);
    int n;
    cin>>n;
    memset(head,-1,sizeof(head));
    cnt = 0;

    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d %d",&u,&v);
        addedge(u,v);
    }
    sum = 0;
    dfs1(1,-1);
    memset(dp,0,sizeof(dp));
    dp[1] = sum;
    ans = 0x3f3f3f3f;
    ans = min(ans,dp[1]);
    dfs2(1,-1);
    printf("%d
",ans);
    for(int i=1;i<=n;i++){
        if(dp[i] == ans)
            printf("%d ",i);
    }

}
View Code
原文地址:https://www.cnblogs.com/acmdeweilai/p/3320455.html