2019 Multi-University Training Contest 8——Acesrc and Travel(树形dp)

original link - http://acm.hdu.edu.cn/showproblem.php?pid=6662

题意:

一棵树,点权为 a − b a-b ab,你先选择一个点作为起点,然后从对面开始依次走,每个点只能走一次。你要值最大,对面要值最小。 最后可以得到的最大值。

解析:

显然固定好位置后,对面先走。有两种走法,向下和向上。

向下很好处理,使用 d o w n [ i ] [ 0 ] down[i][0] down[i][0]表示我从 i i i走到 i i i的儿子后所能得到的最大值, d o w n [ i ] [ 1 ] down[i][1] down[i][1]为欸对面走的最小值,那么有: d o w n [ i ] [ 0 ] = v [ i ] + m a x ( d o w n [ s o n ] [ 1 ] ) , d o w n [ i ] [ 1 ] = v [ i ] + m i n ( d o w n [ s o n ] [ 0 ] ) down[i][0]=v[i]+max(down[son][1]),down[i][1]=v[i]+min(down[son][0]) down[i][0]=v[i]+max(down[son][1]),down[i][1]=v[i]+min(down[son][0])

考虑向上走,走完后,对面的走法也分为向上和向下。

- 向下走时,要不能走当前上去的路,所以存每个点向下走的最值和次值。当最值和上去那条路得出的值相同,就换成次值。
- 向上走时,从上到下一路维护即可。

算法复杂度: O ( n ) O(n) O(n)

代码:

#include
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int maxn=1e5+5;
const LL inf=1000000000000000000ll;
LL a[maxn],b[maxn];

int head[maxn],fa[maxn],to[maxn<<2],nex[maxn<<2],now;
void add(int a,int b){
    nex[++now]=head[a];
    head[a]=now;
    to[now]=b;
}

LL down[maxn][2][2],up[maxn][2];
bool lef[maxn];
void dfs(int p){
    down[p][0][0]=down[p][0][1]=-inf;
    down[p][1][0]=down[p][1][1]=inf;
    lef[p]=1;
    for(int i=head[p];i;i=nex[i]){
        if(to[i]==fa[p])continue;
        lef[p]=0;
        fa[to[i]]=p;
        dfs(to[i]);
        if(down[to[i]][1][0]+a[p]>=down[p][0][0]){
            down[p][0][1]=down[p][0][0];
            down[p][0][0]=down[to[i]][1][0]+a[p];
        }
        else if(down[to[i]][1][0]+a[p]>=down[p][0][1]){
            down[p][0][1]=down[to[i]][1][0]+a[p];
        }

        if(down[to[i]][0][0]+a[p]<=down[p][1][0]){
            down[p][1][1]=down[p][1][0];
            down[p][1][0]=down[to[i]][0][0]+a[p];
        }
        else if(down[to[i]][0][0]+a[p]<=down[p][1][1]){
            down[p][1][1]=down[to[i]][0][0]+a[p];
        }
    }
    if(lef[p]){
        down[p][0][0]=down[p][1][0]=a[p];
    }
}

LL Abs(LL a){
    return a>0?a:-a;
}

void Dfs(int p){
    if(fa[p]==-1){
        up[p][0]=up[p][1]=inf;
    }
    else{
        // up 0
        up[p][0]=inf;
        LL _down=down[fa[p]][1][0];
        if(_down==a[fa[p]]+down[p][0][0])
            _down=down[fa[p]][1][1];
        if(Abs(_down)!=inf)
            up[p][0]=min(up[p][0],a[p]+_down);

        if(fa[p]!=1)
            up[p][0]=min(up[p][0],a[p]+up[fa[p]][1]);

        if(up[p][0]==inf)
            up[p][0]=a[p]+a[fa[p]];

        // up 1
        up[p][1]=-inf;
        _down=down[fa[p]][0][0];
        if(_down==a[fa[p]]+down[p][1][0])
            _down=down[fa[p]][0][1];
        if(Abs(_down)!=inf)
            up[p][1]=max(up[p][1],a[p]+_down);

        if(fa[p]!=1)
            up[p][1]=max(up[p][1],a[p]+up[fa[p]][0]);

        if(up[p][1]==-inf)
            up[p][1]=a[p]+a[fa[p]];
    }
    for(int i=head[p];i;i=nex[i]){
        if(to[i]==fa[p])continue;
        Dfs(to[i]);
    }
}

int main(){
    int t;scanf("%d",&t);
    while(t--){
        memset(head,0,sizeof head);
        now=0;
        int n;scanf("%d",&n);
        rep(i,1,n)scanf("%lld",a+i);
        rep(i,1,n)scanf("%lld",b+i),a[i]-=b[i];
        rep(i,1,n-1){
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        if(n==1){
            printf("%lld\n",a[1]);
            continue;
        }
        fa[1]=-1;
        dfs(1);
        Dfs(1);

        LL ans=-inf;
        for(int i=1;i<=n;i++){
            LL res=inf;
            if(!lef[i])res=min(res,down[i][1][0]);
            if(i!=1)res=min(res,up[i][1]);
            ans=max(ans,res);
        }
        printf("%lld\n",ans);
    }
}

你可能感兴趣的:(DP动态规划)