Rebuilding Roads
题目连接:http://poj.org/problem?id=1947
题意:将一棵n个节点的有根树,删掉一些边变成恰有m个节点的新树。求最少需要去掉几条边。
思路:
f[i][j] 代表以 i 为根的恰有 j 个节点最少需要删掉的边数
对于当前节点 u 的儿子 v ,我们可以取0,1,2,3...
这么一看那么就是简单的树型背包问题
但是有一点比较坑就是:
当 v 我们不取的时候要特殊考虑,根结点 u 必须取
#include <iostream> #include <algorithm> #include <string> #include <string.h> #include <vector> #include <map> #include <stack> #include <set> #include <queue> #include <math.h> #include <cstdio> #include <iomanip> #include <time.h> #include <bitset> #include <cmath> #define LL long long #define INF 0x3f3f3f3f #define ls nod<<1 #define rs (nod<<1)+1 const double eps = 1e-10; const int maxn = 3000 + 10; const LL mod = 1e9 + 7; int sgn(double a){return a < -eps ? -1 : a < eps ? 0 : 1;} using namespace std; struct edge { int v,nxt; }e[maxn<<1]; int head[maxn]; int cnt; int f[maxn][maxn]; int root[maxn]; int n,m; inline void add_edge(int u,int v) { e[++cnt].v = v; e[cnt].nxt = head[u]; head[u] = cnt; } inline void dfs(int x) { f[x][1] = 0; for (int i = head[x];~i;i = e[i].nxt) { int v = e[i].v; dfs(v); for (int j = m;j >= 1;j--) { f[x][j] = f[x][j] + 1; for (int k = 1;k < j;k++) { f[x][j]=min(f[x][j],f[v][k]+f[x][j-k]); } } } } int main() { while (cin >> n >> m) { cnt = 0; memset(head,-1, sizeof(head)); for (int i = 1;i <= n;i++) { for (int j = 0;j <= m;j++) f[i][j] = INF; } for (int i = 1;i < n;i++) { int u,v; cin >> u >> v; add_edge(u,v); root[v] = 1; } int ans = 0; for (int i = 1;i <= n;i++) { if (!root[i]) { dfs(i); ans = f[i][m]; } } for (int i = 1;i <= n;i++) { ans = min(ans,f[i][m]+1); // 如果当前节点不是真正的根节点,那么还要去掉他和他父亲之间的一条边 } cout << ans << endl; } return 0; }