POJ 1947 Rebuilding Roads(树形DP)
题意:
给定 n 个点的树,最少删多少边,会有棵 p 个节点的树。
思路:
1. dp[u][p] 表示以 u 为根节点保留 p 个节点最少删除的边数。初始化的地方比较巧妙:dp[u][1] = 0,其他赋值 INFS
2. 如果保留 u 的孩子节点 v,则有 dp[u][p] = min(dp[u][p - k] + dp[v][k]),如果不保留则有 dp[u][p] = dp[u][p] + 1
3. 此题的时间复杂度为 O(p2 * n) ,不能采取有交集的泛化物品的并的 O(p * n) 的解法,因为对于子树,相对的背包容量并不固定。
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 160;
const int INFS = 0x3fffffff;
int dp[MAXN][MAXN], U[MAXN], V[MAXN];
bool vis[MAXN];
void treedp(int u, int vol, int n)
{
for (int v = 0; v <= vol; ++v)
dp[u][v] = INFS;
dp[u][1] = 0;
for (int i = 1; i < n; ++i)
{
if (u != U[i])
continue ;
treedp(V[i], vol, n);
for (int v = vol; v >= 0; --v)
{
int ans = INFS;
if (dp[u][v] != INFS)
ans = dp[u][v] + 1;
for (int p = 0; p <= v; ++p)
if (dp[u][p] != INFS && dp[V[i]][v - p] != INFS)
ans = min(ans, dp[u][p] + dp[V[i]][v - p]);
dp[u][v] = ans;
}
}
}
int main()
{
int n, p;
while (scanf("%d %d", &n, &p) != EOF)
{
memset(vis, false, sizeof(vis));
for (int i = 1; i < n; ++i)
{
scanf("%d %d", &U[i], &V[i]);
vis[V[i]] = true;
}
int rt;
for (int i = 1; i <= n; ++i)
if (!vis[i])
rt = i;
treedp(rt, p, n);
int ans = dp[rt][p];
for (int i = 1; i <= n; ++i)
if (dp[i][p] < ans)
ans = dp[i][p] + 1;
printf("%d\n", ans);
}
return 0;
}
-------------------------------------------------------
kedebug
Department of Computer Science and Engineering,
Shanghai Jiao Tong University
E-mail: kedebug0@gmail.com
GitHub: http://github.com/kedebug
-------------------------------------------------------