题目链接:BZOJ – 1907
题目分析
使用树形 DP,f[x][0] 表示以 x 为根的子树不能与 x 的父亲连接的最小路径数(即 x 是一个折线的拐点)。
f[x][1] 表示以 x 为根的子树可以与 x 的父亲连接的最小路径数。
转移的方式非常巧妙,Orz PoPoQQQ 的 blog 。
代码
#include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; const int MaxN = 10000 + 5; int T, n; int f[MaxN][2]; struct Edge { int v; Edge *Next; } E[MaxN * 2], *P = E, *Point[MaxN]; inline void AddEdge(int x, int y) { ++P; P -> v = y; P -> Next = Point[x]; Point[x] = P; } inline int gmin(int a, int b) {return a < b ? a : b;} void Solve(int x, int Fa) { f[x][0] = f[x][1] = 1; int Temp = 0; for (Edge *j = Point[x]; j; j = j -> Next) { if (j -> v == Fa) continue; Solve(j -> v, x); f[x][0] = gmin(f[x][0] + f[j -> v][0], f[x][1] + f[j -> v][1] - 1); f[x][1] = gmin(f[x][1] + f[j -> v][0], Temp + f[j -> v][1]); Temp += f[j -> v][0]; } } int main() { scanf("%d", &T); for (int Case = 1; Case <= T; ++Case) { memset(E, 0, sizeof(E)); P = E; memset(Point, 0, sizeof(Point)); scanf("%d", &n); int a, b; for (int i = 1; i <= n - 1; ++i) { scanf("%d%d", &a, &b); AddEdge(a, b); AddEdge(b, a); } Solve(1, 0); printf("%d\n", f[1][0]); } return 0; }