Information Disturbing HDU - 3586(树形dp + 二分查找)

In the battlefield , an effective way to defeat enemies is to break their communication system. 
The information department told you that there are n enemy soldiers and their network which have n-1 communication routes can cover all of their soldiers. Information can exchange between any two soldiers by the communication routes. The number 1 soldier is the total commander and other soldiers who have only one neighbour is the frontline soldier. 
Your boss zzn ordered you to cut off some routes to make any frontline soldiers in the network cannot reflect the information they collect from the battlefield to the total commander( number 1 soldier). 
There is a kind of device who can choose some routes to cut off . But the cost (w) of any route you choose to cut off can’t be more than the device’s upper limit power. And the sum of the cost can’t be more than the device’s life m. 
Now please minimize the upper limit power of your device to finish your task. 

Input

The input consists of several test cases. 
The first line of each test case contains 2 integers: n(n<=1000)m(m<=1000000). 
Each of the following N-1 lines is of the form: 
ai bi wi 
It means there’s one route from ai to bi(undirected) and it takes wi cost to cut off the route with the device. 
(1<=ai,bi<=n,1<=wi<=1000) 
The input ends with n=m=0. 

Output

Each case should output one integer, the minimal possible upper limit power of your device to finish your task. 
If there is no way to finish the task, output -1.

Sample Input

5 5
1 3 2
1 4 3
3 5 5
4 2 6
0 0

Sample Output

3

题目大意:

给出N个节点,N-1条边的树,给出每条边权值w,现在要求切断其中一些边,使得任意一个叶子没有走到祖先(1)的路 给出m,要求切断的边的总权值小于等于m,求所有的方案中切断的最大的权最小的值。

解决思路:

刚开始一直在想怎么直接求出答案,想了半个小时发现走不通,才想到二分枚举答案。。我们二分最大权的值,一直往小了找。

每次限定最大权,然后看最终这种方案是否合法,也就是最终得到的权值和是否小于m。我们用树形dp来判断方案是否合法,dp[u]代表以u为根节点的子树的任意节点无法到达u的最小花费。那么转移方程分为两种情况:

(1)当 w <= limit 时 :         //limit为我们枚举的最大边权

          dp[u] += min(dp[v], w);       //u代表当前节点,v代表u的儿子节点,w为u -> v的权值。

(2)当 w > limit 时:      

          dp[u] += dp[v];

值得一提的是所有叶子节点的dp值要设置成无穷,在此题中,因为m最大为1e6,所以无穷设置成1e7左右就完全ok,当然设置大一点也没问题,但是要注意是否会溢出,建议定义成long long int类型。

建树我用的是链式前向星,听说vector会超时。

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 1010;

int head[maxn], top, n, m;
int dp[maxn];

struct Edge {
    int v, w;
    int next;
}edge[maxn * 2];

inline void add(int u, int v, int w)      //链式前向星
{
    edge[top].v = v;
    edge[top].w = w;
    edge[top].next = head[u];
    head[u] = top++;
}

inline void Init()
{
    top = 0;
    memset(head, -1, sizeof(head));
}

void dfs(int u, int father, int limit)    //树形dp
{
    dp[u] = 1100000;           //相当于初始化为无穷大
    int temp = 0;
    int cnt = 0;             //用来判断是否为叶子节点
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].v;
        int w = edge[i].w;
        if(v != father)
        {
            cnt++;
            dfs(v, u, limit);
            if(w <= limit)         //如果小于枚举的数
            {
                temp += min(dp[v], w);
            }
            else
                temp += dp[v];
        }
    }
    if(cnt != 0)           //排除叶子节点
        dp[u] = temp;
}

int Bin(int l, int r)
{
    int ans = -1;         //设置成-1,所有的都不合法直接返回也没有问题
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        dfs(1, 0, mid);
        if(dp[1] > m)                 //如果不合法,加大最大边权的限制
        {
            l = mid + 1;
        }
        else
        {
            ans = mid;               //记录下最小权值
            r = mid - 1;
        }
    }
    return ans;
}

int main()
{
    //freopen("in.txt", "r", stdin);
    while(cin >> n >> m)
    {
        if(n == 0 && m == 0)
        {
            break;
        }
        Init();
        int u, v, w;
        for(int i = 1; i <= n - 1; ++ i)
        {
            scanf("%d%d%d", &u, &v, &w);
            add(u, v, w);
            add(v, u, w);
        }
        int ans = Bin(1, m);
        printf("%d\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/aqa2037299560/article/details/85061333