【POJ 1947】Rebuilding Roads(树型DP)

时间:2022-07-16 18:45:48
Time Limit: 1000MS   Memory Limit: 30000K
Total Submissions: 10607   Accepted: 4863

Description

The cows have reconstructed Farmer John's farm, with its N barns (1 <= N <= 150, number 1..N) after the terrible earthquake last May. The cows didn't have time to rebuild any extra roads, so now there is exactly one way to get from any given barn to any other barn. Thus, the farm transportation system can be represented as a tree.

Farmer John wants to know how much damage another earthquake could do. He wants to know the minimum number of roads whose destruction would isolate a subtree of exactly P (1 <= P <= N) barns from the rest of the barns.

Input

* Line 1: Two integers, N and P

* Lines 2..N: N-1 lines, each with two integers I and J. Node I is node J's parent in the tree of roads.

Output

A single line containing the integer that is the minimum number of roads that need to be destroyed for a subtree of P nodes to be isolated.

Sample Input

11 6
1 2
1 3
1 4
1 5
2 6
2 7
2 8
4 9
4 10
4 11

Sample Output

2

Hint

[A subtree with nodes (1, 2, 3, 6, 7, 8) will become isolated if roads 1-4 and 1-5 are destroyed.]

Source


卡了一阵子……只能说对树型dp的理解还不是非常深刻……

题目大意:一棵树,问最少砍几条边,能得到一个p个节点的子树。

这样可以想到用dp[i][j]表示节点i为根,得到j个节点的子树所需要砍掉的最少边数。

这样对于父节点u,遍历孩子节点v1.2.3...时,每当新加一个子节点v,可以选取v中0.1.2.3....k(k <= v子树的节点数) 这样对于dp[u][i] 可以通过dp[u][j]外加从v中选取的节点(i-j个)来组成,也就是转移方程。


如果上面的想明白了,会发现有新的问题,dp[i][j]表示以i为根的j个节点的子树所砍掉的边数,但不能包含i和其父亲(如果i不是根)的边,否则在遍历i的父亲那层中会导致转移出错,因为在这层,它与i之间的边不可砍掉(除非整个i子树都不选取)。所以我的方法就是做了一堆啰嗦的判断……


大体这样取个最小值就行了,要注意p个节点的子树可以以原树中任何一个节点为根,也就是答案要在遍历的过程中找。


代码如下:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define fread() freopen("in.in","r",stdin)
#define fwrite() freopen("out.out","w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const int mod = 1e9+7;
const double eps = 1e-8;

bool can[155][155];
int dp[155][155];
int n,p,mn;

void dfs(int u)
{
	memset(dp[u],INF,sizeof(int)*n);
	dp[u][0] = dp[u][1] = 0;
	bool f = 0;
	for(int i = 1; i <= n; ++i)
	{
		if(!can[u][i]) continue;
		f = 1;
		dfs(i);
		for(int k = p; k >= 1; --k)
		{
			if(dp[u][k] != INF) dp[u][k]++;
			for(int j = 1; j <= p && j < k && dp[i][j] != INF; ++j)
			{
				if(dp[u][k-j] != INF) dp[u][k] = min(dp[u][k-j]+dp[i][j],dp[u][k]);
			}
		}
	}
	//如果u不是整个树的根,那么在dp数组中不计入其与父节点的边(为了方便转移),在取答案时需要加上(断绝它与父节点的联系)
	if(u != 1) mn = min(mn,dp[u][p]+1);
	else mn = min(mn,dp[u][p]);
}

int main()
{
	//fread();
	//fwrite();

	int u,v;
	while(~scanf("%d%d",&n,&p))
	{
		memset(can,0,sizeof(can));
		for(int i = 1; i < n; ++i)
		{
			scanf("%d%d",&u,&v);
			can[u][v] = 1;
		}
		mn = INF;
		dfs(1);
		printf("%d\n",mn);
	}

	return 0;
}