(POJ 1947)Rebuilding Roads <树型DP>

来源:互联网 发布:dnf制卡辅助与端口辅助 编辑:程序博客网 时间:2024/05/29 15:49

Rebuilding Roads
Description

The cows have reconstructed Farmer John’s farm, with its N barns (1 <= N <= 150, number 1..N) after the terrible earthquake last May. The cows didn’t have time to rebuild any extra roads, so now there is exactly one way to get from any given barn to any other barn. Thus, the farm transportation system can be represented as a tree.

Farmer John wants to know how much damage another earthquake could do. He wants to know the minimum number of roads whose destruction would isolate a subtree of exactly P (1 <= P <= N) barns from the rest of the barns.
Input

  • Line 1: Two integers, N and P

  • Lines 2..N: N-1 lines, each with two integers I and J. Node I is node J’s parent in the tree of roads.
    Output

A single line containing the integer that is the minimum number of roads that need to be destroyed for a subtree of P nodes to be isolated.
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.]
Source

USACO 2002 February

题意:

n个节点构成一个树,现在删去其中一些边,一个树就变成几个树。问至少去掉几条边才能使得森林中有棵树的节点数为p。

分析:

状态:f(i, j) 表示子树i,保留j个节点的最少删边次数, 注意,这里保留的j个  节点的子树,是指根节点为i的且有j个节点的子树.初始化:对于子树i, 如果只保留1个节点,那么连接它所有儿子节点的边都要删掉,所以可以初始化 f(i, 1) = 节点i的儿子个数f(i, j), 即子树i保留j个节点, 那么对于i的每个子树,可以选择保留1,2,..j-1个节点i的子树v选择保留k个点的话,那么子树i的其他部分就要保留j-k个点.在这里i的子树v选择保留k个点,这k个节点最后会被保留在i的子树上,所以子树i的其他部分就要保留j-k个点(他减去了i与子树v相连的一条边,即多减了一条边),所以dp[i][j] = dp[edge[i][v]][k] + dp[i][j-k] - 1所以由状态转移:f(i, j) = min{ min{f(i,j-k) + f(v, k) - 1 | 1<=k<s} | v是i的儿子节点 }  注意:对于f( i , j ) 如果 i 不是整个树的根,那么还要去掉这个节点跟父亲节点的边。所以ans = min( f(1,p),f( i,p )+1 ). 

AC代码:

#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <algorithm>#include <cmath>#include <vector>using namespace std;const int maxn = 160;vector<int> edge[maxn];int dp[maxn][maxn];int num[maxn];int n,p;int dfs(int u){    num[u] = 1;    for(int i=0;i<edge[u].size();i++) num[u] += dfs(edge[u][i]);    dp[u][1] = edge[u].size();    for(int i=0;i<edge[u].size();i++)//u为根的子树的第i个孩子节点    {        for(int j=num[u];j>=1;j--)//u为根的子树保留j个节点        {            for(int k=1;k<=num[edge[u][i]] && k<j;k++)//edge[u][i]为根的子树保留k个节点            {                if(dp[u][j] > dp[edge[u][i]][k] + dp[u][j-k] - 1)                    dp[u][j] = dp[edge[u][i]][k] + dp[u][j-k] - 1;            }        }    }    return num[u];}int main(){    while(scanf("%d%d",&n,&p)!=EOF)    {        int u,v;        for(int i=0;i<=n;i++) edge[i].clear();        for(int i=1;i<n;i++)        {            scanf("%d%d",&u,&v);            edge[u].push_back(v);        }        memset(dp,0x3f3f3f3f,sizeof(dp));        dfs(1);        int ans = dp[1][p];        for(int i=2;i<=n;i++)        {            if(num[i] >= p && ans > dp[i][p]+1) ans = dp[i][p] + 1;//+1: i 不是整个树的根,那么还要去掉这个节点跟父亲节点的边        }        printf("%d\n",ans);    }    return 0;}
1 0
原创粉丝点击