POJ3585 Solution

题目链接

题解

并不是网络流

状态:(dp_i)表示以(1)为根节点时,(i)子树到节点(i)的最大流量。(f_i)表示以(i)为根节点时的最大流量。

转移方程:设(in_i)表示节点(i)的度数,(fa_i)表示节点(i)的父节点,(e_i)表示与节点(i)相连的边集,(v_i)表示边(i)的终点,(w_i)表示边(i)的流量。

[dp_i=sum min(dp_{v_j},w_j)quad (jin e_i,v_j ot=fa_i)\ f_{v_j}=dp_{v_j}+w_jquad(jin e_i,in_i=1)\ f_{v_j}=dp_{v_j}+min(w_j,f_i-min(w_j,dp_{v_j}))quad (jin e_i,in_i>1) ]

(dp)的转移方程同网络流。可以将水流反向,看作叶子节点为源点而根结点为汇点。当(in_i=1)时,则换根后(i)为叶子节点,贡献为(w_j)。否则贡献为(w_j)与除(v_j)子树外部分的最小值,而后者为(f_i)减去(v_j)子树对(i)结点的贡献,也就是(f_i-min(w_j,dp_{v_j}))

目标状态:(f_i)

代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#define int long long
using namespace std;
const int N=4e5+10;
int fst[N],nxt[N],v[N],w[N],cnt;
int f[N],dp[N],in[N];
int read()
{
   int s=0,ww=1; char ch=getchar();
   while(ch<'0' || ch>'9') {if(ch=='-') ww=-1; ch=getchar();}
   while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*ww;
}
void add(int x,int y,int z)
{
   v[++cnt]=y,w[cnt]=z,in[y]++;
   nxt[cnt]=fst[x],fst[x]=cnt;
}
void dfs1(int x,int fa)
{
   for(int i=fst[x];i;i=nxt[i])
   {
   	int y=v[i];
   	if(y==fa) continue;
   	dfs1(y,x); 
   	if(in[y]==1) dp[x]+=w[i];
   	else dp[x]+=min(w[i],dp[y]);
   }
}
void dfs2(int x,int fa)
{
   for(int i=fst[x];i;i=nxt[i])
   {
   	int y=v[i];
   	if(y==fa) continue;
   	if(in[x]==1) f[y]=dp[y]+w[i]; 
   	else f[y]=dp[y]+min(w[i],f[x]-min(dp[y],w[i]));
   	dfs2(y,x);
   }
}
signed main()
{
   int t=read(),n,ans,x,y,z;
   while(t--)
   {
   	n=read();
   	for(int i=0;i<=n;i++) fst[i]=dp[i]=f[i]=in[i]=0;
   	cnt=ans=0;
   	for(int i=1;i<n;i++)
   	{
   		x=read(),y=read(),z=read();
   		add(x,y,z),add(y,x,z);
   	}
   	dfs1(1,0);		
   	f[1]=dp[1]; dfs2(1,0);
   	for(int i=1;i<=n;i++) ans=max(ans,f[i]);
   	printf("%lld
",ans);
   }
   return 0;
}
原文地址:https://www.cnblogs.com/violetholmes/p/14823143.html