[BZOJ 1907] 树的路径覆盖 【树形DP】

题目链接: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;
}

  

posted @ 2015-04-14 16:16  JoeFan  阅读(305)  评论(0编辑  收藏  举报