POJ 1947——Rebuilding Roads【树形背包】

题目传送门


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

USACO 2002 February


题意

将一棵n个节点的有根树,删掉一些边变成恰有m个节点的新树。求最少需要去掉几条边。

扫描二维码关注公众号,回复: 8675990 查看本文章

题解

  • 首先可以明确是一个树形dp题目,状态也很好定义:
    dp【root】【j】:以root为根节点的子树,得到 j 个节点的子树需要最少减掉的边数,注意子树中必须保留root节点。否则无法dp
    那么很明显的边界条件dp【root】【1】 = num(儿子的个数),因为要只剩一个节点的子树,那么所有的孩子都减掉,这样就为儿子的个数。
  • 那么状态转移方程呢
    dp【root】【i】 = min(dp【root】【i-k】+dp【child】【k】 - 1,dp【root】【i】);
    其实就是要得到一个i个节点的子树,枚举所有的孩子为k个节点的,当前root保留 i-k 个节点,然后把root和child之间之前被剪断的连接起来,所以这里要减1
    注意一些边界条件就OK了
  • 需要额外寻找根节点

AC-Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <fstream>
#include <utility>
using namespace std;
typedef long long ll;
typedef pair<int, int> Pii;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int INF = 0x7fffffff;
const int MAXN = 3e2 + 5;
const int MOD = 1e9 + 7;

struct Edge {
	int next;
	int to;
}edge[MAXN << 1];
int head[MAXN];
int cnt;
int dp[MAXN][MAXN];//dp[i][j]:i为根的树中,删去多少遍能变成j个节点
int son[MAXN];
int num[MAXN];//儿子个数
int fa[MAXN];
int n, m;
void addEdge(int u, int v) {
	edge[cnt].to = v;
	edge[cnt].next = head[u];
	head[u] = cnt++;
}
void dfs(int u, int fa) {
	son[u] = 1;
	for (int i = head[u]; ~i; i = edge[i].next) {
		int v = edge[i].to;
		if (v == fa)	continue;
		dfs(v, u);
		son[u] += son[v];
		for (int j = son[u]; j > 0; j--) {
			for (int k = 1; j - k > 0; k++) {
				dp[u][j] = min(dp[u][j], dp[u][j - k] + dp[v][k] - 1);
			}
		}
	}
}
int main() {
	while (cin >> n >> m) {
		memset(head, -1, sizeof head);
		memset(fa, -1, sizeof fa);
		memset(dp, 0x3f, sizeof dp);
		memset(num, 0, sizeof num);
		cnt = 0;
		for (int i = 1; i < n; i++) {
			int a, b;
			cin >> a >> b;
			addEdge(a, b);
			fa[b] = a;
			num[a]++;
		}
		int root;
		for (root = 1; fa[root] != -1; root = fa[root]);
		for (int i = 1; i <= n; i++)
			dp[i][1] = num[i];
		dfs(root, -1);
		int ans = dp[root][m];
		for (int i = 2; i <= n; i++)
			ans = min(ans, dp[i][m] + 1);
		cout << ans << endl;
	}
	return 0;
}
发布了104 篇原创文章 · 获赞 60 · 访问量 5888

猜你喜欢

转载自blog.csdn.net/Q_1849805767/article/details/103115891