题意
1号点为总司令点,叶子节点为士兵点,要求所有士兵点不能接受到总司令的指令。剪断一条线路有花费,总花费不能超过M,单次花费上限不能超过limit,问limit最小为多少。
题解
首先分析题目,题目中提到了四个状态,点,单次花费,总花费,剪断方式。首先的话,我们可以分析,花费由于数据范围很大,所以是不能作为动态规划的状态的,我们考虑用二分去枚举单次花费,总花费我们考虑作为DP的结果,如果总花费大于花费上限,那么这种选择方案就不可以。至于点,肯定是要作为状态的。最后我们需要慎重考虑剪断方式的问题。
剪断方式分为两种,一种是剪断这个点与子节点的那条连接边,另一种方案是不做处理,等子节点进行处理。看起来好像是需要开一个大小为2的数组来记录这两种状态,这样做当然没问题,不过我们仔细分析一下就可以发现还有更好的解决方案。我们换一种思考方式,对于一个点来说,它这棵子树上的叶子节点究竟以一个什么样的方式与他断开联系并不是很重要,重要的是断开与叶子节点的联系需要多少花费。我们可以把无法断开设置为INF,这样的话,我们在处理最终结果的时候,看见INF就知道这种情况是无解的。
对于当前的与子节点的处理,我们有两种情况,第一种情况是node.val<=limit,也就是说这条边可以剪断,那么我们就取node.val(剪断这条边),dp[v](交给子节点处理)的最小值。第二种情况是node.val>limit,也就是说不能剪断这条边,那么就只能交给子节点处理了。在实际编码的过程中,我们还需要防止在计算的过程中出现溢出的问题,所以需要针对dp[v]>=INF的情况特殊处理一下,这样的话就会产生四种情况,分别处理就可以了。
代码
#include <bits/stdc++.h>
#define UP(i,l,h) for(int i=l;i<h;i++)
#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)
#define W(t) while(t)
#define MEM(a,b) memset(a,b,sizeof(a))
#define MAXN 2010
#define INF 0x3f3f3f3f
#define LL long long
#define int LL
using namespace std;
struct Node {
int to,val;
Node(int to,int val):to(to),val(val) {}
};
vector<Node> vc[MAXN];
int m;
int dp[MAXN];
void dfs(int u,int p) {
dp[u]=0;
int sz=vc[u].size();
bool vis=false;
UP(i,0,sz) {
Node nd=vc[u][i];
if(nd.to==p) continue;
vis=true;
dfs(nd.to,u);
if(nd.val<=m){
if(dp[nd.to]<INF) dp[u]+=min(dp[nd.to],nd.val);
else dp[u]+=nd.val;
}else{
if(dp[nd.to]<INF) dp[u]+=dp[nd.to];
else dp[u]=INF;
}
}
if(!vis) dp[u]=INF;
}
main() {
int n,mx;
W(~scanf("%I64d%I64d",&n,&mx)) {
if(n+mx==0) break;
MEM(vc,0);
int l=1,r=1;
UP(i,0,n-1) {
int a,b,w;
scanf("%I64d%I64d%I64d",&a,&b,&w);
vc[a].push_back(Node(b,w));
vc[b].push_back(Node(a,w));
r=max(r,w);
}
int ans=-1;
W(l<=r) {
m=(l+r)/2;
MEM(dp,INF);
dfs(1,0);
if(dp[1]>mx) {
l=m+1;
} else {
ans=m;
r=m-1;
}
}
printf("%I64d\n",ans);
}
}