POJ 1947 Rebuilding Roads

Description

The cows havereconstructed Farmer John's farm, with its N barns (1 <= N <= 150, number1..N) after the terrible earthquake last May. The cows didn't have time torebuild any extra roads, so now there is exactly one way to get from any givenbarn to any other barn. Thus, the farm transportation system can be representedas a tree. 

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

Input

* Line 1: Twointegers, N and P 

* Lines 2..N: N-1 lines, each with two integers I and J. Node I is node J'sparent in the tree of roads.
 

Output

A single linecontaining the integer that is the minimum number of roads that need to bedestroyed 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

题目大意:有n个点,现在要得到只含有p个点的子树。接着输入n-1条边将n个点相连。问要截断最少截断多少条边能得到只含有p个点的子树。

 

此题首先可以转化一下。每个点与之相连的点有num[i]个,则,得到含有p个点的子树需要截断的边为num[i]-2*(p-1)i为这p个点的编号。式子的得到是,得到的子树内部一共有p-1条边,而∑num[i]将每条边算了两次,即2*(p-1),要得到这个子树就需要截断这个子树外部的边,外部边的数目即,这p个点所含边数的总和减去2*内部的边的数目。而2*(p-1)对于问题来说是固定的。故现在问题就转化成了求p个点所含边数的总和的最小值。

 

dp[i][j]表示的是含有以i点为根节点的子树含有i点的连续j个点所含边数的总和的最小值。我们可以以任意一个点作为树的根节点进行遍历,每次只用计算其子节点即可。同时,一个问题是dp[i][1]要赋初值为num[i]dp[i]中必须含有点i

转移方程:dp[i][k] = min(dp[i][k],dp[i][j] + dp[re][k-j]);(rei的子节点)

 

#include <iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#define INF 0x03F3F3F3F
using namespace std;

int n ,p;
vector<int> v[160];
int num[160];
int dp[160][160] ,flag[160];

void solve(int x)
{
    flag[x] = 1;
    for(int i = 0;i < num[x];i++)
    {
        if(!flag[v[x][i]])
        {
            solve(v[x][i]);
            for(int k = p;k>=1;k--)
            {
                for(int j = k;j>=1;j--)
                {
                    if(dp[x][k] > dp[x][j] + dp[v[x][i]][k-j])
                    {
                        dp[x][k] = dp[x][j] + dp[v[x][i]][k-j];
                    }
                }
            }
        }
    }
}

int main()
{
    int u ,s ,m;
    while(~scanf("%d%d",&n,&p))
    {
        memset(flag,0,sizeof(flag));
        memset(dp,0x03F3F3F3F,sizeof(dp));
        for(int i = 1;i<n;i++)
        {
            scanf("%d%d",&u,&s);
            v[u].push_back(s);
            v[s].push_back(u);
        }
        for(int i = 1;i<=n;i++)
        {
            num[i] = v[i].size();
        }
        for(int i = 1;i<=n;i++)
        {
            dp[i][1] = num[i];
        }
        solve(1);
        m = INF;
        for(int i = 1;i<=n;i++)
        {
            if(m > dp[i][p])
            {
                m = dp[i][p];
            }
        }
        printf("%d\n",m-2*(p-1));
        for(int i = 0;i<=n;i++)
        {
            v[i].clear();
        }
    }
    return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值