标签:
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