[关闭]
@Acqua 2019-01-25T06:18:58.000000Z 字数 2071 阅读 931

HDU3586 Information Disturbing(Jan. 24th, 2019) 树形DP

动态规划 二分答案 独立解题

题目来源

题意

给定一棵结点数为的树,要求砍掉一些边(带非负权),使所有叶结点与根结点不连通。限制最高花费为,求砍掉的边中最大权值的最小值。

思路

尝试极端方法:严格证明法。

Part 1(整体框架)

“最大值最小化”,
使用二分答案
数据结构为树,
使用图论模板
启发式思维[1]
使用树形DP:设表示对于以结点为根的子树,以删除子边删除父边的决策使所有叶结点与根结点不连通的总代价。

Part 2(DP部分)

删除父边就不用管下面的连接情况,
(证伪:“只选择”或“继承的选择”都可以断绝的子树中所有叶结点与的联系)
不删父边就要删掉所有子边(易证:对于有子节点的点,其子节点一定是叶结点或叶结点的祖先),

最终答案为,因为根结点没有父边。
更正:dp[u]表示对于以结点为根的子树,使所有叶结点与根结点不连通的总代价。
对于,答案为

Part 3(二分部分)



不可删除,
在搜索更新时把暂时赋值为
叶结点不可删除子边,
叶结点

  1. ,说明小了,以至于无法删去某些效果能够“一条顶多条”的大权边,否则按照规则,非最优解不可能成为答案,故
  2. ,说明刚好,故
  3. ,说明可能还不够小,以至于删除了一些过大的边取得了更优结果,故

因为是非法结果,所以不参与答案统计,所以把的更新安排在一起。

反思

  1. 理清思路再写题,没有无法战胜的困难,只是没有仔细设计方法。
  2. 以最严谨的态度写代码,不允许任何错误。

代码

  1. // La Pluie
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<iostream>
  5. #include<algorithm>
  6. using namespace std;
  7. const int N=1e3+5,inf=1e6+1;
  8. struct edge{
  9. int v,w,next;
  10. }e[N<<1];
  11. int head[N],k,dp[N],n,m;
  12. void adde(int u,int v,int w){
  13. e[k]=(edge){v,w,head[u]};
  14. head[u]=k++;
  15. }
  16. void dfs(int u,int f,int d){
  17. int son=0;
  18. for(int i=head[u];i!=-1;i=e[i].next){
  19. int v=e[i].v,w=e[i].w;
  20. if(v==f) continue;
  21. son++;
  22. dfs(v,u,d);
  23. dp[u]+=min(dp[v],w>d?inf:w);
  24. }
  25. if(!son) dp[u]=inf;
  26. }
  27. int main(){
  28. while(~scanf("%d%d",&n,&m)&&(n||m)){
  29. memset(head,-1,sizeof(head));
  30. k=1;
  31. for(int i=1;i<n;i++){
  32. int u,v,w;
  33. scanf("%d%d%d",&u,&v,&w);
  34. adde(u,v,w);adde(v,u,w);
  35. }
  36. int l=1,r=1000,ans=-1;
  37. while(l<=r){
  38. int mid=l+r>>1;
  39. memset(dp,0,sizeof(dp));
  40. dfs(1,0,mid);
  41. if(dp[1]<=m) ans=mid,r=mid-1;
  42. else l=mid+1;
  43. }
  44. printf("%d\n",ans);
  45. }
  46. return 0;
  47. }

[1] 需要解析。
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注