2018牛客多校赛第二场 H Travel(树形dp)

版权声明:本文为博主原创文章,转载请著名出处 http://blog.csdn.net/u013534123 https://blog.csdn.net/u013534123/article/details/82930221

大致题意:给你一棵树,每个点上有一个点权,让你找三条不相交的链,使得这三条链的权值和最大。

典型的树形dp,但是状态不太好表示。很容易想到,令dp[i][j]表示在点i以及对应子树里面取了j条链的最大权值和。但是在转移的时候,有可能出现一个点的不同儿子中的链合并的情况,仅仅这样表示状态并不能很好的表示转移。所以我增加一维,令dp[i][j][0]表示在点i取了j条链的最大权值和,令dp[i][j][1]表示在点i取了j条链,同时外加一条包含i点的链的最大权值和,也即实际上选择了j+1条链,而且前j条链里面,没有包括点i。

然后在转移的时候,根据合并的情况,需要有辅助的数组。对于当前点x,令a[i][j]表示目前已经选择了i条链,而且x的度是j的最大权值和。这里之所以要考虑x的度,是因为要考虑两个儿子的合并,点x可以与两个儿子的链合并成为一条链,或者加入其中一个儿子的链中,因此这里的度最多可以是2。在转移的时候,度就体现在dp[son][i][0]和dp[son][i][1]的选择,如果选择dp[0],那么度没有贡献,选择dp[1]那么度有贡献。可能会说,选择dp[0]的时候有可能也包含选择了son的链,为什么dp[0]的时候没有贡献呢?因为当度等于2时,链的条数就要减去一个1,但是如果是选择了dp[0],在转移的时候,原本的两个包含儿子的链都要被计算,不能做到链的条数减一,没有真正意义上的合并。另外,在转移的时候,不能让当前更新的点马上对转移产生影响,所以要弄一个辅助数组b。有了这些,我们就可以写出转移方程:

         \large b[i+j][k+l]=max(b[i+j][k+l],a[i][k]+dp[son][j][l])

然后我们看最后答案怎么确定。对于dp[x][i][0],可以两种方式转移,一是不选择x点,那么直接就是a[i][0];二是选择x点并且可以与一些儿子的链合并变成新的一条链,那么dp[x][i][0]=max(dp[x][i][0],a[i-1][j]+w[x])。可能会问为什么是a[i-1],根据之前说的,我转移a[i][j]的时候只是用了dp[0],也就是说计算度对应的链并没有算到链的条数里面,所以我在这里如果取了w[x],那么就要把这条链加进去,所以只能是链数减一。对于dp[x][i][1],要求是选择i条链,然后还要再加一条包含x的链。所以有dp[x][i][1]=max(a[i][j]+w[x]),但是这里必须要保证j<2,因为如果j==2,意味着我有两条链的权值和都算进去了,但是我这里dp[1]只是能多一条链,链的数目对不上。其他情况相当于说把多的一条链放到了这个点上面。

按照这样转移即可。最后的答案就是dp[1][3][0],具体见代码:

#include<bits/stdc++.h>
#define PI 3.1415926535
#define mod 998244353
#define LL long long
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define INF 0x3f3f3f3f
#define sf(x) scanf("%d",&x)
#define sc(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define clr(x,n) memset(x,0,sizeof(x[0])*(n+5))
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
using namespace std;

const int N = 400010;

std::vector<int> g[N];
LL dp[N][4][2];
int w[N],n;

inline void upd(LL &x,LL y){x=x>y?x:y;}

void dfs(int x,int fa)
{
    LL a[4][3]={0},b[4][3]={0};
    for(auto y:g[x])
    {
        if (y==fa) continue;
        dfs(y,x); memset(b,0,sizeof(b));
        for(int i=0;i<=3;i++)
            for(int j=0;i+j<=3;j++)
                for(int k=0;k<=2;k++)
                    for(int l=0;l<=1&&l+k<=2;l++)
                        upd(b[i+j][k+l],a[i][k]+dp[y][j][l]);
        memcpy(a,b,sizeof(a));
    }
    for(int i=0;i<=3;i++)
    {
        upd(dp[x][i][0],a[i][0]);
        for(int j=0;j<=2;j++)
        {
            if (i) upd(dp[x][i][0],a[i-1][j]+w[x]);
            if (j!=2) upd(dp[x][i][1],a[i][j]+w[x]);
        }
    }
}

int main()
{
    sf(n);
    for(int i=1;i<=n;i++) sf(w[i]);
    for(int i=1;i<n;i++)
    {
        int x,y; sf(x); sf(y);
        g[x].pb(y); g[y].pb(x);
    }
    dfs(1,0);
    printf("%lld\n",dp[1][3][0]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/82930221
今日推荐