题目链接: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; }