Solved:3
rank:187
H.travel
题意:给一颗带有点权的树 找三条不相交的链 使得点权最大
题解:使用树形DP dp[x][i][0/1] 表示x节点选择i条链 有没有经过x的链
对于每一个回溯的状态 下面的结点已经处理好了
但是处理当前结点时还有一种有两条经过x的链 实际上他们可以合成一条链
为了避免重复计算 经过当前结点的权值在更新时计算
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; int n; ll val[400005]; ll dp[400005][4][2]; vector<int> g[400005]; void dfs(int x, int fa) { ll tmp[4][3]; ll f[4][3]; for(int i = 0; i < 4; i++) tmp[i][0] = tmp[i][1] = tmp[i][2] = 0; for(int i = 0; i < g[x].size(); i++) { int v = g[x][i]; if(v == fa) continue; dfs(v, x); memcpy(f, tmp, sizeof(tmp)); for(int a = 0; a < 4; a++) for(int b = 0; b < 4; b++) { if(a + b > 3) continue; for(int c = 0; c < 3; c++) for(int d = 0; d < 2; d++) { if(c + d > 2) continue; tmp[a + b][c + d] = max(tmp[a + b][c + d], f[a][c] + dp[v][b][d]); } } } for(int i = 0; i < 4; i++) { dp[x][i][0] = max(dp[x][i][0], tmp[i][0]); dp[x][i][1] = max(dp[x][i][1], tmp[i][1] + val[x]); if(i < 3) { for(int j = 0; j < 2; j++) for(int k = j + 1; k < 3; k++) dp[x][i + 1][j] = max(dp[x][i + 1][j], tmp[i][k] + val[x]); } } } int main() { scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%lld", &val[i]); for(int i = 1; i < n; i++) { int u, v; scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); } dfs(1, -1); ll ans = 0; for(int i = 0; i < 4; i++) ans = max(ans, dp[1][i][0]); printf("%lld ", ans); return 0; } /* 13 10 10 10 10 10 1 10 10 10 1 10 10 10 1 2 2 3 3 4 4 5 2 6 6 7 7 8 7 9 6 10 10 11 11 12 11 13 */