标签:
| Time Limit: 1000MS | Memory Limit: 30000K | |
| Total Submissions: 10066 | Accepted: 4595 |
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
[A subtree with nodes (1, 2, 3, 6, 7, 8) will become isolated if roads 1-4 and 1-5 are destroyed.]
题意:有N个点和N-1条边构成的树,问你最少删去几条边使得新树中节点数为P。
思路:用dp[i][j]表示以i节点为根的树 中选中j个节点 最少需要删去的边数。
我们利用树形dp的思想,考虑u节点的子节点v。
一:直接去掉<u,v>边即去掉以v为根的子树,显然有dp[u][j] + 1;
二:保留边<u,v>,那么有dp[u][j-k] + dp[v][k],(1<= k <= j)。对于该情况我们要求出(1<=k<=j)范围的最小值再和第一种情况比较。
这样得到状态转移方程
dp[u][v] = min ( min(dp[u][j-k]+dp[v][k] ) , dp[u][v] + 1).
AC代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define MAXN 200
#define INF 0x3f3f
using namespace std;
vector<int> G[MAXN];
int dp[MAXN][MAXN];//dp[i][j]存储以i节点为根的树 选j个点 最少删的边数
int pre[MAXN];
int N, P;
void init()
{
for(int i = 1; i <= N; i++)
G[i].clear(), pre[i] = i;
}
void getMap()
{
int a, b;
for(int i = 1; i < N; i++)
{
scanf("%d%d", &a, &b);
G[a].push_back(b);
pre[b] = a;
}
}
int num[MAXN];
void DFS(int u)
{
dp[u][1] = 0;//初始 自己不删边
for(int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
DFS(v);
for(int j = P; j >= 0; j--)
{
int t = dp[u][j] + 1;//直接删掉 与 子节点v 相连的边
for(int k = 0; k <= j; k++)
t = min(t, dp[u][j-k] + dp[v][k]);
dp[u][j] = t;
}
}
}
void solve()
{
int root;
for(int i = 1; i <= N; i++)
{
if(pre[i] == i)
{
root = i;
break;
}
}
memset(dp, INF, sizeof(dp));
DFS(root);
int ans = INF;
for(int i = 1; i <= N; i++)
{
if(i == root)//删边后 原根节点 依旧在
ans = min(ans, dp[i][P]);
else//删边后 原根节点已经没了 加上去掉根的一条边
ans = min(ans, dp[i][P] + 1);
}
printf("%d\n", ans);
}
int main()
{
while(scanf("%d%d", &N, &P) != EOF)
{
init();
getMap();
solve();
}
return 0;
}版权声明:本文为博主原创文章,未经博主允许不得转载。
poj 1947 Rebuilding Roads 【树形DP】 【求至少删去树中 多少条边 使得树中节点数为P】
标签:
原文地址:http://blog.csdn.net/chenzhenyu123456/article/details/47670777