题目:给定n个敌方据点,1为司令部,其他点各有一条边相连构成一棵树,每条边都有一个权值cost表示破坏这条边的费用,叶子节点为前线。现要切断前线和司令部的联系,每次切断边的费用不能超过上限limit,问切断所有前线与司令部联系所花费的总费用少于m时的最小limit。(1<=n<=1000,1<=m<=1e6)
二分limit,dp[u]表示去除了子树u的所有叶节点的最小花费,dp[u]=min(w[u],sigma(dp[v])); w[u]表示切断u与其父节点之间的边权,dp[v]为子节点的最优值。
#include <bits/stdc++.h>
using namespace std;
const int maxn=1010;
#define inf 1000001坑死人啊
int n,m;
struct node{
int x,w;
node(int _x,int _w)
{
x=_x;
w=_w;
}
};
vector<node>a[maxn];
int limit,dp[maxn];
void dfs(int u,int fa)
{
int flag=0,tmp=0;
for(int i=0;i<a[u].size();i++)
{
int v=a[u][i].x,w=a[u][i].w;
if(v==fa) continue;
flag=1;
if(w<=limit) dp[v]=w;//直接剪断
dfs(v,u);
tmp+=dp[v];//计算sigma(dp[v])
}
if(flag==1)//转移是在u不是叶子节点的情况下进行的
dp[u]=min(dp[u],tmp);//min函数里面的dp[u]是暂时存的直接剪断时的花费值
}
bool check(int fuck)
{
limit=fuck;
for(int i=1;i<=n;i++)
dp[i]=inf;inf不要取太大,否则之后用到的加和会爆int,1e6+1刚刚好
dfs(1,-1);
return dp[1]<=m;///
}
int main()
{
//cout << "Hello world!" << endl;
//freopen("in.txt","r",stdin);
while(~scanf("%d%d",&n,&m),n+m)
{
for(int i=1;i<=n;i++)
a[i].clear();
for(int i=1;i<n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
a[x].push_back(node(y,z));
a[y].push_back(node(x,z));
}
int l=0,r=m,ans=-1;
while(l<r-1)
{
int mid=(l+r)/2;
if(check(mid))
r=mid;
else l=mid;
}
if(check(r)) ans=r;
if(check(l)) ans=l;
printf("%d\n",ans);
}
return 0;
}