题目链接
题意挺奇怪的,最初的时候还以为是道网络流的题目,后来想了想,就是求剪去所有叶子节点到根节点的关系的所需最贵的一条边最小化的题目,挺拗口的不是吗?
这道题,想了良多,听说有用树形DP+二分来写的(之后再补上这样的做法吧),但我不那么做,我想了想,树形DP+链式前向星也可以写,剪去非根节点以外的边,我们所需的价值不就是要么剪去与父亲的边,要么就是剪去所有与儿子的边的权值的总和,所以dp[i][j]存放的是对于i这个节点,我们处理掉它与它的子节点或者父节点关系的最小花费,j的意思是你用j的钱是否可以达成目的,以及达成目的后的最优解,就是j~上限的所需花费可能会是同一个值,然后我定义了个cost[]数组,从叶子节点开始向上返回,返回所要消去这号链上的节点所需要的最小花费,其中,从叶子节点往上,我们的花费得从最小下限开始,如果低于了最小下限,说明钱不够,所以得有这样的判断。
初始化问题:这里的dp[][]得初始化为极大值,INF一开始写成(long long)1<<63,会WA,改一下,写成INF=0x3f3f3f3f就过了,我也不知道是什么原因,下次规范着写吧,就像前段时间刚学的链式前向星一样(略懂略懂==)。
完整代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef long long ll;
const int maxN=1005;
const ll INF=0x3f3f3f3f;
int N, M, cnt, head[maxN<<1], mx;
ll dp[maxN][maxN];
struct Eddge
{
int to, next, val;
Eddge(int a=0, int b=-1, int c=0):to(a), next(b), val(c) {}
}edge[maxN<<1];
void addEddge(int u, int v, int val)
{
edge[cnt]=Eddge(v, head[u], val);
head[u]=cnt++;
}
void dfs(int u, int r)
{
if(u!=1) for(int i=r; i<=mx; i++) dp[u][i]=r;
ll cost[maxN]={0};
for(int i=head[u]; i!=-1; i=edge[i].next)
{
int v=edge[i].to, val=edge[i].val;
dfs(v, val);
for(int j=1; j<=mx; j++)
{
cost[j]+=dp[v][j];
}
}
for(int i=1; i<=mx; i++)
{
if(cost[i]==0) continue;
dp[u][i]=min(dp[u][i], cost[i]);
}
}
int main()
{
while(scanf("%d%d", &N, &M) && (N | M))
{
memset(head, -1, sizeof(head)); cnt=mx=0;
for(int i=1; i<N; i++)
{
int e1, e2, e3;
scanf("%d%d%d", &e1, &e2, &e3);
addEddge(e1, e2, e3);
mx=max(mx, e3);
}
for(int i=1; i<=N; i++)
{
for(int j=1; j<=mx; j++)
{
dp[i][j]=INF;
}
}
dfs(1, 0);
int ans=-1;
for(int i=1; i<=mx; i++)
{
if(dp[1][i]<=M) { ans=i; break; }
}
printf("%d\n", ans);
}
return 0;
}