树形 DP 入门
WHAT
树本身是靠递归定义的,这对于 dp 来说十分滴友好(本身就具有递归结构)
Problem Expressions
Independent Set (Maximum)
TO vertexes
define
对于任意一个图 \(G\) ,确定一个边集 \(V'\) ,使得其中的点两两不相邻
co-NP
Dominating Set (Minimum)
TO vertexes
definition
每个点的覆盖范围一定,在图 \(G\) 中选出一个点集 \(V'\) ,使得 \(G\) 中的每个点都被覆盖
co-NP
Vertex Cover (Minimum)
TO vertexes
definition
每个点可覆盖所有相邻的边,在图 \(G\) 中选出一个点集 \(V'\) ,使得 \(G\) 中的每条边都被覆盖
co-NP
Matching (Maximum)
TO edges
definition
在图 \(G\) 中选出一个点集 \(E'\) ,使得这些边两两不共点
NP-hard
Addition
在特殊条件限制下(例如树、二分图),以上四个问题可以在多项式时间内解决
Problem Solutions
Solution 1st
定义 dp[u][0/1] 表示节点 u 选没选
则很容易得出来以下转移式:
View Code
void DFS(int u, int father) {
dp[u][0] = 0;
dp[u][1] = wealth[u];
for (int v : adj[u]) {
if (v == father) continue;
DFS(v, u);
dp[u][0] += max(dp[v][0], dp[v][1]);
dp[u][1] += dp[v][0];
}
}
Solution 2nd
definition
dp[u][0] :被自己覆盖
dp[u][1]:被子节点覆盖
dp[u][2]:被父节点覆盖
State Transformation Function
View Code
void DFS(int u, int father) {
dp[u][0] = 1;
for (int v : adj[u]) {
if (v == father) continue;
DFS(v, u);
dp[u][0] += min({dp[v][0], dp[v][1], dp[v][2]});
dp[u][2] += min(dp[v][0], dp[v][1]);
}
dp[u][1] = INT_MAX / 3;
for (int v : adj[u]) {
if (v == father) continue;
dp[u][1] = min(dp[u][1], dp[u][2] - min(dp[v][0], dp[v][1]) + dp[v][0]);
}
}
提醒
最后统计答案的时候,由于根节点并没有父亲节点
所以 ans = min(dp[root][0], dp[root][1])
Solution 3rd
稍微转化一下就变得和第一个问题类似
View Code
void DFS(int u, int father) {
dp[u][0] = 0;
dp[u][1] = wealth[u];
for (int v : adj[u]) {
if (v == father) continue;
DFS(v, u);
dp[u][0] += dp[v][1];
dp[u][1] += min(dp[v][0], dp[v][1]);
}
}
Solution 4th
我们会惊奇的发现,思路竟然与第二个问题相同
View Code
void DFS(int u, int father) {
dp[u][0] = 0;
for (int v : adj[u]) {
if (v == father) continue;
DFS(v, u);
dp[u][0] += max({dp[v][0], dp[v][1]});
}
dp[u][1] = INT_MIN / 3;
for (int v : adj[u]) {
if (v == father) continue;
dp[u][1] = max(dp[u][1], dp[u][0] - max(dp[v][0], dp[v][1]) + dp[v][0] + 1);
}
}
树的直径
搜索
前置知识
从树上任意一个节点出发,记与其距离最远的点为 \(s\),\(s\) 出发搜到的与之最远的点为 \(t\) ,路径 \(s \to t\) 即为树的直径
Proof
\(\blacksquare\)
- 当 \(u\) 在直径 \(s \to t\) 上时
假设从 \(s\) 搜到的点并不是 \(t\),记为 \(T\)
则
与 \(s\to t\) 是直径的事实矛盾
-
\(u\) 不在直径上,但是有交点
同理
\(\square\)
DP
观察一条直径 \(s \to t\)
在其上选择一个点 \(u\)
我们会发现 \(u\to s\)、\(u\to t\) 一定是从 \(u\) 出发的最长与次长路径
不放定义 \(dp_{\text{type},u}\) 表示从节点 \(u\) 出发的最长/次长路径长度
记 \(v \in \text{son}(u)\), \(\text{length} = dp_{1,v}+w_{uv}\)
则有:
-
当 \(\text{length} > dp_{1,u}\) 时:
\[dp_{2,u}=dp_{1,u}\\ dp_{1,u}=\text{length} \] -
当 \(dp_{1,u}>\text{length}>dp_{2,u}\) 时:
\[dp_{2,u} = \text{length} \]代码实现就是:
if (length > dp[1][u]) swap(length, dp[1][u]); if (length > dp[2][u]) swap(length, dp[2][u]);

浙公网安备 33010602011771号