POJ 3585 Accumulation Degree
题面:
一棵有n个节点的树,有容量的双向边,求以任意节点为源,能流到叶子节点的最大的流量。
解题过程:
- 二次扫描和换根法
- 前向星记录双向的图和树,在遍历的时候会有往回走的路径,要用额外的数组 vis 记录遍历情况防止“回流”
- 在POJ提交的时候,我的 f 、d 是long long 已经取 min 的时候将一个 int 的数据转为 long long 进行运算,结果 RE 了,把 long long 全部去掉后就好了。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define rep(i,l,p) for(int i=l;i<=p;i++)
#define fread() freopen("in.txt","r",stdin)
typedef pair<int,int> P;
#define Fi first
#define Se second
#define mp make_pair
int head[200005],tot;
int T,n;
struct Edge
{
int nx,v,d;
}edge[400005];
void add(int u,int v,int d){
edge[++tot].nx = head[u]; edge[tot].v = v; edge[tot].d = d;
head[u] = tot;
}
int f[200005];
int d[200005];
bool vis[200005];
int deg[200005];
void dp(int x){
vis[x] = true;
for(int i=head[x];i!=-1;i=edge[i].nx){
int y = edge[i].v;
if(vis[y]) continue;
dp(y);
if(deg[y] == 1){
d[x] += edge[i].d;
}else d[x] += min(edge[i].d,d[y]);
}
return;
}
void dfs(int x){
vis[x] = true;
for(int i=head[x];i!=-1;i=edge[i].nx){
int y = edge[i].v;
if(vis[y]) continue;
if(deg[x] == 1) f[y] = d[y]+edge[i].d;
else{
f[y] = d[y] + min(edge[i].d,f[x] - min(d[y],edge[i].d));
}
dfs(y);
}
}
int main(int argc, char const *argv[])
{
// fread();
scanf("%d",&T);
while(T--){
memset(head,-1,sizeof head); tot =0;
memset(deg,0,sizeof deg);
scanf("%d",&n);
int u,v,dd;
rep(i,1,n-1){
scanf("%d%d%d",&u,&v,&dd);
add(u,v,dd); add(v,u,dd);
deg[v]++,deg[u]++;
}
memset(f,0,sizeof f);
memset(d,0,sizeof d);
memset(vis,false,sizeof vis);
dp(1);
memset(vis,false,sizeof vis);
f[1] = d[1];
dfs(1);
int ans = 0;
rep(i,1,n) ans = max(ans,f[i]);
printf("%d\n",ans);
}
return 0;
}