题意:给你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; }