Information Disturbing (树型DP + 二分)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3586

题意:

给定n个敌方据点,1为司令部,其他点各有一条边相连构成一棵树,每条边都有一个权值cost表示破坏这条边的费用,叶子节点为前线。现要切断前线和司令部的联系,每次切断边的费用不能超过上限limit,问切断所有前线与司令部联系所花费的总费用少于m时的最小limit

思路:

dp[x] 代表 x 为根的子树总的花费

需要求最小上限lim,容易想到二分去求解

状态转移方程怎么去确定?

设当前边权为 w

( lim >= w)    dp[u] += min(dp[v] , w )   (可以选择断了当前的边或者不断)

( lim < w)      dp[u] += dp[v]                 ( 只能选择不断当前边权)

最后去判断下总的花费是否满足题意

#include <iostream>
#include <algorithm>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <math.h>
#include <cstdio>
#include <iomanip>
#include <time.h>
#include <bitset>
#include <cmath>
#include <sstream>

#define LL long long
#define INF 10000100
#define ls nod<<1
#define rs (nod<<1)+1

const double eps = 1e-10;
const int maxn = 2e4 + 10;;
const LL mod = 1e9 + 7;

int sgn(double a){return a < -eps ? -1 : a < eps ? 0 : 1;}
using namespace std;

// 一棵有n个节点的有根树,1为根节点,边带权,
// 表示删掉这条边的代价。现在要删掉一些边,使叶子节点不能到达根节点。
// 但是,每次删除的边的代价不能超过limit,删掉的边的总代价不能超过m
// 求最小的limit的可能取值。

struct edge{
    int v,w,nxt;
}e[maxn<<1];

int head[maxn];
int f[maxn];
int cnt;
int n,m;

inline void add_edge(int u,int v,int w) {
    e[++cnt].v = v;
    e[cnt].w = w;
    e[cnt].nxt = head[u];
    head[u] = cnt;
}

inline void dfs(int x,int fa,int lim) {
    int fl = 1;
    f[x] = 0;
    for (int i = head[x];~i;i = e[i].nxt) {
        int v = e[i].v;
        if (v == fa)
            continue;
        fl = 0;
        dfs(v,x,lim);
        if (e[i].w <= lim)
            f[x] += min(f[v],e[i].w);
        else
            f[x] += f[v];
        if (f[x] >= 10000100)
            f[x] = 10000100;
    }
    if (fl)
        f[x] = 10000100;
}

inline bool check(int x) {
    dfs(1,0,x);
    return (f[1] <= m);
}


int main() {
    ios::sync_with_stdio(false);
    while (cin >> n >> m) {
        if (n == 0 && m == 0)
            break;
        cnt = 0;
        memset(head,-1, sizeof(head));
        for (int i = 1;i < n;i++) {
            int u,v,w;
            cin >> u >> v >> w;
            add_edge(u,v,w);
            add_edge(v,u,w);
        }
        if (n == 1) {
            cout << 0 << endl;
            continue;
        }
        int l = 1,r = 1000;
        int ans = -1;
        while (l <= r) {
            int mid = (l + r) >> 1;
            if (check(mid)) {
                ans = mid;
                r = mid - 1;
            }
            else
                l = mid + 1;
        }
        cout << ans << endl;
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/-Ackerman/p/12363516.html