题意:有一个信息传输网,根结点是司令,叶子节点是前线的士兵,中间的节点是各级军官,每个士兵或军官,有且只有一名直接上司,这样就形成了一个树形结构。士兵或军官只向自己的直接上司报告情报,他们通过电话线进行通话。现在,敌人想切断一些电话线,让司令得不到任何前线士兵发来的情报。切断每根电话线有不同花费
w i
,当工具的能力大于等于
w i
时,才能切断这个电话线。工具也有使用寿命,切断的所有电话线的花费之和不能大于M。
求出最小的工具的能力多少。
思路:因为N不大,可以直接用树形DP来求解。
设dp[u][k]为让以u为根节点子树,节点u收不到叶子节点的信息,用能力为k的工具切割电话线,需要的花费。
状态转移如下:
1.如果
k≥e.w
,可以选择切割这个边,也可以选择不切割这个边。当不切割这个边的时候,对应的花费,有儿子节点v的dp[v][k]加和。
2.如果
k<e.w
,这个时候只能从儿子节点v得到。
即:
dp[u][k]=∑ v∈son(u) {min(dp[v][k],e.w),dp[v][k], k≥e.wk<e.w
边界条件:
dp[u][k]=+∞,u∈leaf
但是这样会TLE。
可以注意到,dp[u][k],在u固定的,以k为变量的时候,是个单调不递增的函数,所以我们可以二分k。
这样的复杂度为: Θ(nlogk)
代码如下:
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX = 1010;
const int INF = 1000010;
struct edge{
int to,w;
edge(){}
edge(int u,int ww):to(u),w(ww){}
} edges[MAX<<1];
int N,M;
int nxt[MAX<<1],head[MAX],tot;
int dp[MAX];
void init()
{
memset(head,-1,sizeof(head));
tot = 0;
}
void addedge(int u, int v, int w)
{
edges[tot] = edge(v,w);
nxt[tot] = head[u];
head[u] = tot++;
}
void dfs(int u, int p,int limit)
{
bool ok = true;
dp[u] = 0;
for(int i = head[u]; ~i; i = nxt[i]){
edge & e = edges[i];
if(e.to == p) continue;
ok = false;
dfs(e.to,u,limit);
if(limit >= e.w)
dp[u] += min(dp[e.to],e.w);
else
dp[u] += dp[e.to];
}
if(ok)
dp[u] = INF;
}
int main(void)
{
//freopen("input.txt","r",stdin);
while(scanf("%d%d",&N,&M),N||M){
init();
int maxx = 0;
for(int i = 1; i <= N-1;++i){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
maxx = max(maxx,w);
}
int lb = 0, ub = maxx;
while(lb + 1 < ub){
int mid = (lb + ub) >> 1;
dfs(1,0,mid);
if(dp[1] <= M)
ub = mid;
else
lb = mid;
}
dfs(1,0,ub);
if(dp[1] <= M)
printf("%d\n",ub);
else
printf("-1\n");
}
return 0;
}