CF633F The Chocolate Spree
CF633F The Chocolate Spree
题意翻译
给出一棵树,每个节点有一个权值,求出不相交的两条链的最大权值和。
题解:
树形DP好题。
众所周知,作为一道DP题,当你没办法转移的时候,你要想着再多来几个状态帮你转移。
比如这道题:
我们直接设答案:\(dp[x][0]\)表示以x为根的子树选出两条不相交链的最大值。
怎么转移呢?
首先设当前节点为\(x\),其中一个儿子为\(y\)。转移要对以下四步取max:
首先,要在以\(y\)为根的子树中选两条不相交链,这可能是答案。然后,在\(y\)为根的子树中选一条,在其他儿子里选一条。再然后,从该点和子树中分别选出半条链,再从子树里选一条完整链。最后,第三条的反向思路也是可以的。
然后发现一时半会没办法维护这四种情况。于是再来几个状态:\(dp[x][0]\)表示以\(x\)为根的子树选出一条链的最大值。\(g[x]\)表示以\(x\)为根的子树中从\(x\)到叶子再加上一条不相交链的最大值。\(down[x]\)表示从\(x\)到叶子节点的最大值。然后为了快速维护第二种情况,再加上\(h[x]\)表示所有儿子的\(dp[y][0]\)最大值。
大功告成,还有亿点点细节。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
const int INF=0x3f3f3f3f;
int n;
vector<int> e[maxn];
int w[maxn];
ll dp[maxn][2],g[maxn],h[maxn],down[maxn];
void dfs(int x, int f)
{
dp[x][0]=dp[x][1]=g[x]=down[x]=w[x];
h[x]=0;
for(int i=0;i<e[x].size();i++)
{
int u=e[x][i];
if(u==f)
continue;
dfs(u,x);
dp[x][0] = max(dp[x][0], dp[u][0]);
dp[x][0] = max(dp[x][0], dp[x][1]+dp[u][1]);
dp[x][0] = max(dp[x][0], down[x]+g[u]);
dp[x][0] = max(dp[x][0], g[x]+down[u]);
dp[x][1] = max(dp[x][1], dp[u][1]);
dp[x][1] = max(dp[x][1], down[x]+down[u]);
g[x] = max(g[x], w[x]+g[u]);
g[x] = max(g[x], down[x]+dp[u][1]);
g[x] = max(g[x], down[u]+w[x]+h[x]);
h[x] = max(h[x], dp[u][1]);
down[x] = max(down[x], down[u]+w[x]);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,0);
printf("%lld",dp[1][0]);
return 0;
}