| Time Limit: 1000MS | Memory Limit: 30000K | |
| Total Submissions: 9499 | Accepted: 4317 |
Description
Input
Output
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
Source
题目链接:http://poj.org/problem?id=1947
题目大意:一颗含有n个结点的树,求减去最少的边使得该树只含有p个结点
题目分析:典型的树形dp,在树上进行动态规划,设dp[s][i]表示以s为根的子树含有i个结点需要删去的边数,则得到转移方程:
对于i的子树k:
1.不加子树k,dp[s][i] = dp[s][i] + 1 (不加子树k,相当于删去一条边)
2.加子树k,dp[s][i] = min(dp[s][j] + dp[k][i - j]) (1<= j <= i) (以s为根的树的结点数加上其子树的结点数等于i)
最后答案就是在dp[i][m]中取小,要注意的一点是,如果i不是根,值还需要+1,因为要脱离原来的根,还要去掉一条边
这里需要注意,dp过程是自下而上的,也就是从叶子到根,而不是从根到叶子
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int const INF = 0x3fffffff;
int const MAX = 155;
struct Edge
{
int to, next;
}e[MAX * MAX / 2];
int n, m;
int head[MAX], cnt, root;
int fa[MAX], dp[MAX][MAX];
void Add(int x, int y) //加边
{
e[cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt++;
}
void DFS(int u)
{
for(int i = 0; i <= m; i++) //初始化为无穷大
dp[u][i] = INF;
dp[u][1] = 0; //根结点本就一个点,不需要减边
for(int i = head[u]; i != -1; i = e[i].next) //同层
{
int v = e[i].to; //u的子树
DFS(v);
for(int j = m; j >= 1; j--)
{
for(int k = 0; k < j; k++)
{
if(k) //不加子树k
dp[u][j] = min(dp[u][j], dp[u][j - k] + dp[v][k]);
else //加上子树k
dp[u][j] = dp[u][j] + 1;
}
}
}
}
int main()
{
scanf("%d %d", &n, &m);
cnt = 0;
memset(fa, -1, sizeof(fa));
memset(head, -1, sizeof(head));
for(int i = 1; i < n; i++)
{
int x, y;
scanf("%d %d", &x, &y);
Add(x, y);
fa[y] = x;
}
for(root = 1; fa[root] != -1; root = fa[root]);
DFS(root);
int ans = dp[root][m];
for(int i = 1; i <= n; i++) //除了根节点,其他节点要想成为独立的根,必先与父节点断绝关系,所以要先加1
ans = min(ans, dp[i][m] + 1);
printf("%d\n",ans);
}POJ 1947 Rebuilding Roads (树形dp 经典题)
原文地址:http://blog.csdn.net/tc_to_top/article/details/44134065