题意 :有一个树形的水系,由N-1条河道和N个交叉点组成。每条河道有个容量
简单来讲就是有课树形湖泊,度数为1的为汇点,现在让你找到一个源点,求从源点流向汇点水系流量最大。
题解:暴力的做法 就是枚举源点然后 求水系的流量时间复杂度为o(n*n)
但如果用二次扫描与换根法即可将时间复杂度降为o(n);
二次扫描与换根法
1 第一次扫描 任选一个点为根,对整颗树执行一树形dp ,自低向上的状态转移
2 第二次扫描 从刚选的根,对整颗树执行一次深度优先遍历,每次实现自顶向下的推导,从而得到答案
对于本题我们设f[x]为表示把x作为源点,流向整个水系,流量最大是多少;
设d[x]为第一次扫描 以root为根,从x出发流向子树的流量
对于根节点 root 有f[root]=d[root]
对与x节点到他的子节点y来讲 如果 x和y的度数都大于一。则f[y]首先有d[y]构成
剩下的部分是由 从y流向x,和x流向其他子节点组成
y流向x则是c(x,y)(x与y边的容量)
x流向其他节点 则是f[x]-min(d[y],c(x,y)) (如果这个不理解可以自己画图看看)
同理对与x的度数等于一
则f[y]=d[y]+c(x,y);
对于y的度数等于一
f[y]=d[y]+min(f[x]-c(x,y),c(x,y));
# include <bits/stdc++.h> using namespace std; int t; int n; const int N=1*1e6; int edge[N],ver[N],Next[N],head[N]; int cnt=0; int f[N]; void add(int a,int b,int c) { ver[++cnt]=b; edge[cnt]=c; Next[cnt]=head[a]; head[a]=cnt; } int fa[N]; int d[N]; int v[N]; void dp(int x) { v[x]=1; d[x]=0; for(int i=head[x]; i; i=Next[i]) { int y=ver[i]; if(v[y]) { continue ; } dp(y); if(fa[y]==1) { d[x]+=edge[i]; } else { d[x]+=min(d[y],edge[i]); } } return ; } void dfs(int x) { v[x]=1; for(int i=head[x]; i; i=Next[i]) { int y=ver[i]; if(v[y]) { continue ; } if(fa[x]==1) { f[y]=d[y]+edge[i]; } else if(fa[y]==1) { f[y]=d[y]+min(f[x]-edge[i],edge[i]); } else { f[y]=d[y]+min(f[x]-min(d[y],edge[i]),edge[i]); } dfs(y); } return ; } int main() { int t; cin>>t; while(t--) { memset(head,0,sizeof(head)); memset(f,0,sizeof(f)); memset(d,0,sizeof(d)); memset(fa,0,sizeof(fa)); memset(v,0,sizeof(v)); cnt=0; cin>>n; for(int i=1; i<=n-1; i++) { int a,b,c; cin>>a>>b>>c; fa[a]++; fa[b]++; add(a,b,c); add(b,a,c); } int root=1; dp(root); memset(v,0,sizeof(v)); f[root]=d[root]; dfs(root); int ans=0; for(int i=1;i<=n;i++) { ans=max(ans,f[i]); } cout<<ans<<endl; } }