1. 程式人生 > >Information Disturbing HDU - 3586(樹形dp + 二分查詢)

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;
}