洛谷 P1272 重建道路

题目链接

题解

树形dp

\(f_{i, j}\)表示以\(i\)为根的子树切出联通块大小为\(j\)的最小答案
显然\(f[i][1]\)为与\(i\)连的边数

\(v\)\(u\)的儿子
那么有\(f[u][i]=f[u][i-j]+f[v][j]-2\),因为\(u->v\)这条边算了两次

  • 注意\(i\)要从大到小枚举

Code

#include<bits/stdc++.h>
#define LL long long
#define RG register
using namespace std;

inline int gi() {
    int f = 1, s = 0;
    char c = getchar();
    while (c != '-' && (c < '0' || c > '9')) c = getchar();
    if (c == '-') f = -1, c = getchar();
    while (c >= '0' && c <= '9') s = s*10+c-'0', c = getchar();
    return f == 1 ? s : -s;
}
int n, p;
const int N = 200;
int f[N][N];
struct node {
	int to, next;
}g[N<<1];
int last[N], gl, siz[N];
inline void add(int x, int y) {
	g[++gl] = (node) {y, last[x]};
	last[x] = gl;
	return ;
}
#define Min(a, b) (a>b?b:a)
inline void dfs(int u, int fa) {
	siz[u] = 1;
	f[u][1] = 0;
	for (int i = last[u]; i; i = g[i].next) {
		int v = g[i].to;
		f[u][1]++;
		if (v == fa) continue;
		dfs(v, u);
		siz[u] += siz[v];
	}
	for (int i = last[u]; i; i = g[i].next) {
		int v = g[i].to;
		if (v == fa) continue;
		for (register int j = siz[u]; j > 1; j--)
			for (register int k = 1; k < j; k++)
				f[u][j] = min(f[u][j], f[v][k]+f[u][j-k]-2);
	}
	return ;
}

int main() {
	n = gi(), p = gi();
	for (int i = 1; i < n; i++) {
		int u = gi(), v = gi();
		add(u, v); add(v, u);
	}
	memset(f, 0x3f, sizeof(f));
	dfs(1, 0);
	int ans = f[1][p];
	for (int i = 2; i <= n; i++)
		ans = Min(ans, f[i][p]);
	printf("%d\n", ans);
    return 0;
}

posted @ 2018-12-22 15:24  zzy2005  阅读(131)  评论(0编辑  收藏  举报