树形dp入门 hdu 1520 and 2196

hdu 1520 

题意:给定一棵关系树,每个节点有个权值,子节点和父节点不能同时选,问最后能选的最大价值是多少?

题解:树形DP入门题。由于子节点与父节点不能同时选,有人可能会用贪心思想,二者选其一肯定最优。其实不然,有可能父节点和子节点都不选,而要选子孙节点。不过只要再往深点想下,就可以得出动态规划的解法。每个节点要么选要么不选,和大多数选不选动归一样,来个dp[i][2],0表示不选,1表示不选,那我们只要从叶子节点往根结点不断更新dp[i][0]和dp[i][1]就可以了。 

    状态转移方程:dp[i[[1] = sum(dp[j][0])                      (当前选了,子节点必定不能选,最优的情况是都不选,然后累加)

                          dp[i][0] = sum(max(dp[i][0],dp[i][1]))   (当选不选,子节点可选可不选,找大的那个状态)

struct Edge{
    int v,next;
}edge[MAX*2];
int dp[MAX][2];
int vis[MAX];
int head[MAX];
int n;
int tot;

void add_edge(int a,int b){
    edge[tot]=(Edge){b,head[a]};
    head[a]=tot++;
}
void tree_dp(int u){
    vis[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(!vis[v]){
            tree_dp(v);
            dp[u][1]+=dp[v][0];
            dp[u][0]+=max(dp[v][1],dp[v][0]);
        }
    }
}

int main(){
    while(~scanf("%d",&n)){
        mem0(dp);
        mem0(vis);
        mem1(head);
        tot=0;
        for(int i=1;i<=n;i++) scanf("%d",&dp[i][1]);
        int a,b;
        while(scanf("%d%d",&a,&b)&&a&&b){
            add_edge(a,b);
            add_edge(b,a);
        }
        tree_dp(1);//把1当作根
        printf("%d\n",max(dp[1][1],dp[1][0]));
    }
    return 0;
}
hdu 2196

题意:给你n-1条边,每条边有权值,然后求从任意一个点开始到底的最长路径

题解:这题和树的直径挺像的,求从i点开始的最长路径,这个路径要么经过i的父节点,要么经过i的子节点,所以可以用两次dfs来维护从i开始的最长路径和次长路径(为什么要记录次长路,看代码理解比较快,因为在考虑从它父节点过来的距离的时候,如果它就是它父节点上的最长路径上的一点,那么必须考虑父节点的次长路径)

struct Edge{
    int v,cost,next;
}edge[MAX*2];
int maxn[MAX];
int maxid[MAX];
int smaxn[MAX];
int smaxid[MAX];
int head[MAX];
int n;
int tot;

void add_edge(int a,int b,int c){
    edge[tot]=(Edge){b,c,head[a]};
    head[a]=tot++;
}

void dfs1(int u,int fa){
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        int c=edge[i].cost;
        if(v==fa) continue;
        dfs1(v,u);
        if(smaxn[u]<maxn[v]+c){
            smaxn[u]=maxn[v]+c;
            smaxid[u]=v;
            if(smaxn[u]>maxn[u]){
                swap(smaxn[u],maxn[u]);
                swap(maxid[u],smaxid[u]);
            }
        }
    }
}

void dfs2(int u,int fa){
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        int c=edge[i].cost;
        if(v==fa) continue;
        if(v==maxid[u]){
            if(smaxn[v]<c+smaxn[u]){ 
                smaxn[v]=c+smaxn[u];
                smaxid[v]=u;
                if(smaxn[v]>maxn[v]){
                    swap(smaxn[v],maxn[v]);
                    swap(maxid[v],smaxid[v]);
                }
            }
        }
        else{
            if(smaxn[v]<c+maxn[u]){
                smaxn[v]=c+maxn[u];
                smaxid[v]=u;
                if(smaxn[v]>maxn[v]){
                    swap(smaxn[v],maxn[v]);
                    swap(maxid[v],smaxid[v]);
                }
            }
        }
        dfs2(v,u);
    }
}

int main(){
    while(~scanf("%d",&n)){
        mem1(head);
        mem0(smaxn);
        mem0(maxn);
        mem0(maxid);
        mem0(smaxid);
        tot=0;
        for(int i=2;i<=n;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            add_edge(i,a,b);
            add_edge(a,i,b);
        }
        dfs1(1,-1);
        dfs2(1,-1);
        for(int i=1;i<=n;i++) printf("%d\n",maxn[i]);
    }
    return 0;
}



你可能感兴趣的:(ACM)