原题传送门
这真是一道难题qwq
看了%%%Claris的题解后懂了
树形DP
先设置状态,
d
p
[
u
]
[
i
]
[
j
]
dp[u][i][j]
dp[u][i][j]表示以u为根的子树中黑点
i
i
i个,白点
j
j
j个,的答案
首先优化空间,
i
>
=
1
i>=1
i>=1的情况等价于
i
=
1
i=1
i=1,反正都是不满足要求的
j
>
=
2
j>=2
j>=2的情况等价于
j
=
2
j=2
j=2,这样就把空间大大的减少
考虑转移
先把子树的信息综合到自己身上
再把自己的信息考虑进去
方程具体见代码
转移的时候需要分层,不能同一层中直接转,会wa
分层就直接多开一个数组记录一下
注意多组数据,别忘了初始化
Code:
#include <bits/stdc++.h>
#define maxn 300010
#define LL long long
using namespace std;
const LL inf = 1LL << 60;
struct Edge{
int to, next, len;
}edge[maxn << 1];
LL dp[maxn][3][3], f[3][3], ans;
int num, head[maxn], color[maxn], n;
inline int read(){
int s = 0, w = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
return s * w;
}
void addedge(int x, int y, int z){ edge[++num] = (Edge){ y, head[x], z }; head[x] = num; }
void upd(LL &x, LL y){ if (x > y) x = y; }
void dfs(int u, int pre){
for (int i = 0; i <= 1; ++i) for (int j = 0; j <= 2; ++j) dp[u][i][j] = inf;
dp[u][0][0] = 0;
for (int x = head[u]; x; x = edge[x].next){
int v = edge[x].to;
if (v != pre){
dfs(v, u);
for (int i = 0; i <= 1; ++i) for (int j = 0; j <= 2; ++j) f[i][j] = inf;
for (int i = 0; i <= 1; ++i)
for (int j = 0; j <= 2; ++j)
if (dp[u][i][j] < inf)
for (int k = 0; k <= 1; ++k)
for (int l = 0; l <= 2; ++l)
if (dp[v][k][l] < inf){
upd(f[i | k][min(j + l, 2)], dp[u][i][j] + dp[v][k][l]);
if (!k || l < 2) upd(f[i][j], dp[u][i][j] + dp[v][k][l] + edge[x].len);
//把一棵子树断掉需要满足!k||l<2
}
for (int i = 0; i <= 1; ++i) for (int j = 0; j <= 2; ++j) dp[u][i][j] = f[i][j];
}
}
for (int i = 0; i <= 1; ++i) for (int j = 0; j <= 2; ++j) f[i][j] = inf;
for (int i = 0; i <= 1; ++i)
for (int j = 0; j <= 2; ++j)
if (dp[u][i][j] < inf) upd(f[i | !color[u]][min(2, j + (color[u] == 1))], dp[u][i][j]);
//把自己综合进去
for (int i = 0; i <= 1; ++i) for (int j = 0; j <= 2; ++j) dp[u][i][j] = f[i][j];
}
int main(){
int M = read();
while (M--){
num = 0;
memset(head, 0, sizeof(head));
n = read();
for (int i = 1; i <= n; ++i) color[i] = read();
for (int i = 1; i < n; ++i){
int x = read(), y = read(), z = read();
addedge(x, y, z); addedge(y, x, z);
}
dfs(1, 0);
ans = inf;
for (int i = 0; i <= 1; ++i)
for (int j = 0; j <= 2; ++j)
if (!i || j < 2) upd(ans, f[i][j]);
//这边就直接用f数组了,其实dp[1][i][j]也一样
//同样需要满足!i||j<2
printf("%lld\n", ans);
}
return 0;
}

本文深入解析树形动态规划的原理与应用,通过一道难题的解答过程,详细阐述了如何设置状态、优化空间以及实现状态转移。文章分享了从理解题目到编写代码的全过程,适合对动态规划和树形数据结构感兴趣的读者。
1472

被折叠的 条评论
为什么被折叠?



