标签:
题目大意:
大意就是给你一棵树(n<=1000),树的根恒定为1,然后你需要删掉一些边,使得任何叶子都没有路径到达结点1(根).
删边的要求是在删掉的边的总花费不超过m的情况下,最大的花费最小。
解题报告:
首先本题卡了stl的vector,所以需要手写前向星链表.
因为本题中的 m 太大,而单条边的花费最多只有1000,所以我们不妨有 dp( i , j )表示将i为根的子树没有任何叶子可以到达点i,且其中的最大花费是m的最小总边权花费。
那么我们考虑转移,考虑到现在已经处理完了点i的前 k - 1 个子树,现在正在处理点i的第 k 个子树,我们将前面的状态进行转移。
不妨有这条边的花费为 w ,
我们考虑 j 从 0 -> w 的转移,他们的转移只有两种选择,第一种还是维护最大花费为 j , 此时肯定不能切掉这条w的边,只能在子树中切掉他,还有一种就是切掉w这条边,状态转移到dp(u,w_
之后我们考虑 j 从 w + 1 -> 1000 的转移,他们的转移就是取min好了,第一种是在子树中切掉,还有一种是切掉这条w的边,这样我们转移就完成了,初始化dp都是0(因为还没有考虑子树)
同时叶子的话dp初始化成inf,注意转移的时候inf+inf.....可能会爆int,需要谨慎的进行判断.
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int maxn = 1e3 + 15; struct Edge { int v , w , nxt; }; int n , m ,dp[maxn][maxn],temp[maxn] , head[maxn],tot,maxv,sz[maxn]; Edge e[maxn*2]; void add(int u ,int v ,int w) { e[tot].v = v , e[tot].nxt = head[u] , e[tot].w = w;sz[u]++; head[u] = tot ++ ; } void initiation() { memset(head,-1,sizeof(head));tot=0;maxv=0;memset(sz,0,sizeof(sz)); for(int i = 1 ; i < n ; ++ i){int u , v , w;scanf("%d%d%d",&u,&v,&w);add(u,v,w);add(v,u,w);maxv=max(maxv,w);}memset(dp,0x00,sizeof(dp)); } inline void updata(int & x,int v) {x = min(x,v);} void dfs(int u ,int fa) { if(sz[u] == 1 && u != 1) memset(dp[u],0x3f,sizeof(dp[u])); for(int i = head[u] ; ~i ; i = e[i].nxt) { int v = e[i].v , w = e[i].w; if(v == fa) continue;dfs(v,u);int newe=1<<30; for(int j = 0 ; j <= w ; ++ j) { updata(newe,dp[u][j]+w); if(dp[u][j] >= 1000005 || dp[v][j] >= 1000005) dp[u][j] = 1000005; else dp[u][j] += dp[v][j]; }dp[u][w]=newe; for(int j = w + 1 ; j <= maxv ; ++ j) dp[u][j] += min(dp[v][j] , w); } } int solve() { dfs(1,0);int ans = 1<<29; for(int i = 0 ; i <= maxv ; ++ i) if(dp[1][i] <= m) {ans = i;break;} if(ans == (1<<29)) ans = -1; return ans; } int main(int argc , char * argv[]) { while(scanf("%d%d",&n,&m) && n) { initiation(); printf("%d\n",solve()); } return 0; }
HDU 3586 : Information Disturbing
标签:
原文地址:http://www.cnblogs.com/Xiper/p/4833781.html