《算法竞赛进阶指南》0x54树形DP POJ3585积蓄程度

题目链接:http://poj.org/problem?id=3585

给定一棵树,边上面代表的是流量,可以选定一个点作为源点,一个点作为汇点,汇点的出量无限,源点的入量无限,问选择哪一个点能使得树中这个点为源点的流量最大。

采用的算法是二次扫描与换根法,第一次扫描的时候选择一个root自底向上算出d的值,也就是从这个点出发向下的最大流量,一次扫描后root结点的流量是确定的,复制到f[root]中,接下来对自顶向下更新子树的f数组的值。更新的时候用上已经是真实f的父节点的f数组值以及当前点的d数组的值。注意在父节点和子节点是叶子节点的时候要单独计算f函数的值,因为实际上叶子结点计算的d数组值是0,但是实际上应该是inf,所以在d数组是非真实的时候要单独计算。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 200010;
const int inf=0x3f3f3f3f;
int n;
int head[maxn],nxt[maxn<<1],len[maxn<<1],ver[maxn<<1];
int f[maxn],d[maxn];
int deg[maxn];
int cnt=0;
void addedge(int a,int b,int c){
    ver[++cnt]=b;
    len[cnt]=c;
    nxt[cnt]=head[a];
    head[a]=cnt;
}
void dfs_d(int u,int fa){
    d[u]=0;
    for(int i=head[u];i;i=nxt[i]){
        int v=ver[i];
        int w=len[i];
        if(v==fa)continue;
        dfs_d(v,u);
        if(deg[v]==1)d[u]+=w;
        else d[u]+=min(d[v],w);
    }
}
void dfs_f(int u,int fa){
//u及以上的部分f数组已经计算完毕, 
//通过父节点更新子节点的信息 
    for(int i=head[u];i;i=nxt[i]){
        int v=ver[i];
        int w=len[i];
        if(v==fa)continue;
        if(deg[u]==1)f[v]=d[v]+w;//父节点是叶子结点 
        else if(deg[v]==1)f[v]=min(f[u]-w,w);
        else f[v]=d[v]+min(f[u]-min(d[v],w),w);
            dfs_f(v,u);//计算以v为根节点的子树的所有f 
    }
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        cnt=0;
        scanf("%d",&n);
        memset(head,0,sizeof(head));
        memset(deg,0,sizeof deg);
        for(int i=0;i<n-1;i++){
            
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            addedge(a,b,c);
            addedge(b,a,c);
            deg[a]++,deg[b]++;
        }
        memset(d,0,sizeof(d));
        dfs_d(1,-1);//算出d数组 
        f[1]=d[1];
        dfs_f(1,-1);
        int res=0;
        for(int i=1;i<=n;i++)res=max(res,f[i]);
        
        printf("%d\n",res);
    }
    return 0;
} 

 

posted @ 2020-07-31 20:48  WA自动机~  阅读(189)  评论(0编辑  收藏  举报