POJ 3585

http://poj.org/problem?id=3585

题目

有棵无根树,给出每一条边的最大流量,可以得到以点i为源时流过每个点的最大流量,可以随意调整i,求整个树所有的点的流量中的最大值。

题解

从每个点出发,发出的流量不好计算,因为有后效性。任意选一个根,如果从叶子出发,设dp[x]为以x为源时,x的流量,那么

x为叶子时dp[x]=0

考虑x的儿子t

如果不是叶子,$dp[x]+=min(dp[t],v(x,t)$

如果是叶子,$dp[x]+=v(x,t)$

然后还需要换根,设以x为根的流量是f[x]

每次换根

$f[t]=dp[t]+f[x]-min(dp[t],v(x,t)$

但是如果x在换了以后变成叶子也需要单独考虑

AC代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cassert>
#define REP(i,a,b) for(register int i=(a); i<(b); i++)
#define REPE(i,a,b) for(register int i=(a); i<=(b); i++)
#define PERE(i,a,b) for(register int i=(a); i>=(b); i--)
using namespace std;
#define MAXN 200007
int hd[MAXN], nxt[MAXN*2], to[MAXN*2], v[MAXN*2], en;
int deg[MAXN];
int dp[MAXN], f[MAXN];
bool vis[MAXN];
int n;
inline void adde(int a, int b, int c) {
	nxt[en]=hd[a]; hd[a]=en; to[en]=b; v[en]=c; en++;
}
void calc(int x) {
	dp[x]=0;
	vis[x]=1;
	for(int i=hd[x]; ~i; i=nxt[i]) {
		int t=to[i];
		if(!vis[t]) {
			if(deg[t]==1) {
				dp[t]=0;
				vis[t]=1;
				dp[x] += v[i];
			} else {
				calc(t);
				dp[x] += min(dp[t], v[i]);
			}
		}
	}
}
void dfs(int x) {
	vis[x]=1;
	for(int i=hd[x]; ~i; i=nxt[i]) {
		int t=to[i];
		if(!vis[t]) {
			if(deg[x]==1) {
				f[t]=dp[t]+v[i];
			} else {
				f[t]=dp[t]+min(f[x]-min(dp[t],v[i]), v[i]);
			}
			dfs(t);
		}
	}
}
int main() {
	int T; scanf("%d", &T);
	while(0<T--) {
		en=0;
		scanf("%d", &n);
		memset(hd+1, -1, sizeof(int)*n);
		memset(deg+1, 0, sizeof(int)*n);
		REP(i,1,n) {
			int a,b,c; scanf("%d%d%d", &a, &b, &c);
			adde(a,b,c); adde(b,a,c);
			deg[a]++, deg[b]++;
		}
		memset(vis+1, 0, sizeof(bool)*n);
		calc(1);		
		memset(vis+1, 0, sizeof(bool)*n);
		f[1]=dp[1];
		dfs(1);
		int ans=dp[1];
		REPE(i,2,n) if(f[i]>ans) ans=f[i];
		printf("%d
", ans);
	}
}
原文地址:https://www.cnblogs.com/sahdsg/p/12469911.html