题意:给你n个结点,m,然后有n-1行数据,每行三个数据a,b,c,表示切断a,b之间的联系是c。结点1是指挥部,叶子结点为前线,在花费不超过的m的基础上,求切断的点中花费最大的最小值。
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3586
题解思路:不难看出这是一道树形dp,但是这样确定这个极限值,我们可以通过二分枚举,因为两个结点中花费最大为1000,所以我们最多枚举10次,这个复杂度还是可以接受的。状态转移方程:dp[i] += min(cost[i][son], dp[son])或者dp[i] += dp[son], i表示结点i,cost[i][son]表示i结点与孩纸结点之间的花费。前一个方程为cost[i][son]的值小为我们枚举范围中的最大值,第二个自然为大于的情况,因此在状态转移时需要判断一个两者的大小关系。
附上ac代码:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <list>
#include <map>
#include <set>
#define Forn_m(n,m) for(int i = n; i <= m; i++)
#define ForN_m(n,m) for(int i = n; i < m; i++)
#define MAX 1005
#define INF 1e6+50
using namespace std;
struct Edge
{
int v, lenght;
};
int n, m;
int dp[MAX];
bool vis[MAX];
vector <Edge> vec[MAX];
void dfs(int father,int num,int root, int limit)
{
bool flag = false;
int nu = vec[root].size();
vis[root] = true;
ForN_m(0, nu)
{
if (!vis[vec[root][i].v])
{
dfs(root,i,vec[root][i].v, limit);
flag = true;
if (vec[root][i].lenght <= limit)
dp[root] += min(dp[vec[root][i].v], vec[root][i].lenght);
else
dp[root] += dp[vec[root][i].v];
}
}
if (!flag)
if (vec[father][num].lenght <= limit)
dp[root] = vec[father][num].lenght;
else
dp[root] = INF;
}
void dichotomia(int left, int right)
{
memset(vis, 0, sizeof(vis));
memset(dp, 0, sizeof(dp));
int temp = (left + right) >> 1;
dfs(-1,-1,1,temp);
if (left == right)
if (dp[1] <= m)
printf("%d\n", left);
else
printf("-1\n");
else if (dp[1] <= m)
dichotomia(left, temp);
else
dichotomia(temp + 1, right);
}
int main()
{
Edge temp;
int u, t;
while (scanf("%d%d", &n, &m) && (n != 0 || m != 0))
{
Forn_m(1, n)
vec[i].clear();
memset(vis, 0, sizeof(vis));
ForN_m(1, n)
{
scanf("%d%d%d", &u, &temp.v, &temp.lenght);
vec[u].push_back(temp);
t = temp.v;
temp.v = u;
vec[t].push_back(temp);
}
dichotomia(1, 1000);
}
return 0;
}