poj3585 Accumulation Degree 题解报告

港控/mmm° 2021-12-19 04:55 276阅读 0赞

题目传送门

【题目大意】

一个树形水系,有$n$个结点,根结点称为源点,叶子结点称为汇点,每条边都有水量限制$C_{x,y}$($x,y$为这条边的两个端点),源点单位时间流出的水量称为整个水系的流量,求以哪一个结点作为源点整个水系的流量最大。

【思路分析】

这是一道“不定根”的树形DP问题,这类题目的特点是,给定一个树形结构,需要以每个结点为根进行一系列统计。我们一般通过两次扫描来求解此类问题:

1.第一次扫描,任选一个点为根,在“有根树”上执行一次树形DP。

2.第二次扫描,从刚才选出的根出发,对整棵树执行一次DFS,在每次递归前进行自上而下的推导,计算出“换根”之后的解。

首先,我们任选一个结点$root$,然后树形DP一下,求出$D_{root}$数组($D[i]$表示在以$i$为根的子树中流量的最大值)。然后设$f[x]$表示以x为源点,流向整个水系的最大流量,则显然$D[root]=f[root]$。假设$f[x]$已经求出,考虑其子结点$y$,则$f[y]$包含两部分:

1.从$y$流向以$y$为根的子树的流量,已经计算出来。

2.从$y$沿着到父节点$x$的河道,进而流向水系中其他部分的流量。

由题意可得,从$x$流向$y$的流量为$min(D[y],c[x][y])$,所以从$x$流向除$y$以外其他部分的流量就是二者之差$f[x]-min(D[y],c[x][y])$。于是,把$y$作为源点,先流到$x$,再流向其他部分的流量就是把这个“差”再与$c[x][y]$取较小值后的结果。

if(du[x]>1)\to f[y]=D[y]+min(f[x]-min(D[y],c[x][y])-c[x][y])

if(du[x]==1)\to f[y]=D[y]+c[x][y]

这是一个由上而下的递推方程,我们通过一次DFS即可实现。

【代码实现】

ContractedBlock.gif ExpandedBlockStart.gif

  1. 1 #include<iostream>
  2. 2 #include<cstdio>
  3. 3 #include<vector>
  4. 4 #include<cstring>
  5. 5 #define rg register
  6. 6 #define go(i,a,b) for(rg int i=a;i<=b;i++)
  7. 7 #define mem(a,b) memset(a,b,sizeof(a))
  8. 8 using namespace std;
  9. 9 const int N=2e5+2;
  10. 10 int T,n,du[N];
  11. 11 struct node{
  12. int to,w;};
  13. 12 vector<node> c[N];
  14. 13 bool v[N];
  15. 14 int d[N],f[N];
  16. 15 void clean(){
  17. 16 mem(v,0);mem(du,0);mem(d,0);mem(f,0);
  18. 17 go(i,1,n) c[i].clear();
  19. 18 }
  20. 19 void dp(int x){
  21. 20 v[x]=1;
  22. 21 int size=c[x].size()-1;
  23. 22 go(i,0,size){
  24. 23 int to=c[x][i].to,w=c[x][i].w;
  25. 24 if(v[to]) continue;
  26. 25 dp(to);
  27. 26 if(du[to]==1) d[x]+=w;
  28. 27 else d[x]+=min(d[to],w);
  29. 28 }
  30. 29 return;
  31. 30 }
  32. 31 void dfs(int x){
  33. 32 v[x]=1;
  34. 33 int size=c[x].size()-1;
  35. 34 go(i,0,size){
  36. 35 int to=c[x][i].to,w=c[x][i].w;
  37. 36 if(v[to]) continue;
  38. 37 if(du[x]==1) f[to]=d[to]+w;
  39. 38 else f[to]=d[to]+min(f[x]-min(d[to],w),w);
  40. 39 dfs(to);
  41. 40 }
  42. 41 return;
  43. 42 }
  44. 43 int main(){
  45. 44 scanf("%d",&T);
  46. 45 while(T--){
  47. 46 scanf("%d",&n);clean();
  48. 47 int x,y,z;
  49. 48 go(i,1,n-1){
  50. 49 scanf("%d%d%d",&x,&y,&z);
  51. 50 c[x].push_back((node){y,z});c[y].push_back((node){x,z});
  52. 51 du[x]++;du[y]++;
  53. 52 }
  54. 53 dp(1);
  55. 54 f[1]=d[1];mem(v,0);
  56. 55 dfs(1);
  57. 56 int ans=0;
  58. 57 go(i,1,n) ans=max(ans,f[i]);
  59. 58 printf("%d\n",ans);
  60. 59 }
  61. 60 return 0;
  62. 61 }

代码戳这里

转载于:https://www.cnblogs.com/THWZF/p/11010281.html

发表评论

表情:
评论列表 (有 0 条评论,276人围观)

还没有评论,来说两句吧...

相关阅读

    相关 POJ1009解题报告

    保送之后都是项目的事情,一直没有时间写acm题,今天刚好礼拜六尝试着继续之前的工作,争取以后每周能够写上1-2个poj。很久没写算法题感觉自己的智商已经完全不够用了。 说说这

    相关 POJ1007解题报告

    其实就是求线性代数里面所谓的逆序数,既然是逆序数那肯定从后往前计数,通过计算每个字符的逆序数最终算出整个字符串的逆序数。用switch进行条件判断, 比如CAGT,直观上看这

    相关 POJ1005解题报告

    题目的思路就是,告诉了坐标即可求出圆的半径(可求出当前坐标时的面积,由于是半圆所以pi\r^2还要乘上个0.5),除以河流侵蚀的速度50/年,得出结果。 计算面积的时候是do

    相关 POJ1003解题报告

    题目很长,看半天才理解就是 找出一个N 使得 1/2+1/3+1/4+....1/N+1 的值大于某个输入的浮点数值,输出N。 由于题目有最小和最大输入的限制(0.01-5.

    相关 POJ1002解题报告

    原题[点击打开链接][Link 1],网上很多人说是中文的,但是到我做的时候是英文的。题目大概意思就是讲字母可以代换数字,ABC代换2 DEF代换3 依次类推。 思路其实很简

    相关 金字塔 题解报告

    [题目传送门][Link 1] 【题目大意】 整个金字塔为一个有根树结构,根结点为入口,每个结点涂有一种颜色。机器人从入口开始进行DFS,每经过一个结点,它就会记录这个结点