POJ 3585 Accumulation Degree(二次扫描+换根)

题目

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

题目大意:给一棵无根树,求出最大流量

题解

题解一

暴力做法枚举源点 s s s,让 s s s作为整棵树的根,然后就变成了树形 D P DP DP,因为答案的取得与子树有关
f [ x ] f[x] f[x]表示已 x x x为根,把 x x x作为源点,从 x x x向子树出发所获得的最大流量。
d e g r e e [ i ] degree[i] degree[i]表示点 i i i的度
那么方程为
f [ x ] = ∑ s o n ( x ) { min ⁡ ( f [ y ] , c ( x , y ) ) d e g r e e [ y ] > 1 c ( x , y ) d e g r e e [ y ] = 1 f[x]= \sum_{son(x)} \begin{cases} \min(f[y],c(x,y)) & degree[y] > 1\\ c(x,y)&degree[y]=1\end{cases} f[x]=son(x){min(f[y],c(x,y))c(x,y)degree[y]>1degree[y]=1
时间复杂度为 O ( n 2 ) O(n^2) O(n2)

code1

void dp(int x) {
	vis[x] = true; 
	f[x] = 0; 
	for (int i = lin[x]; i; i = edge[i].next) {
		int y = edge[i].to; 
		if (vis[y]) continue; 
		dp(y); 
		if (deg[y] == 1) f[x] += edge[i].dis; 
		else f[x] += min(f[y], edge[i].dis); 
	}
}

int main() {
	for (int i = 1; i <= n; ++i) {
		memset(f, 0x3f, sizeof(f)); 
		memset(vis, false, sizeof(vis));
		dp(i);  
		ans = max(ans, f[i]); 
	}
	printf("%d\n", ans);  
}

题解2

本题是一道不定根的树型 D P DP DP,我们可以通过两次扫描来求解此类题目
1、第一次扫描时选取任意一点为根,在“有根树”上执行树形 D P DP DP,也就是在回溯时发生的,自底向上的状态转移
2、第二次扫描时,从刚才选的根出发, d f s dfs dfs遍历整棵树,在递归前自上而下的推导,计算换根后的解


具体实现过程为首先任选一个源点作为根节点,记为 r o o t root root,利用解法一来 D P DP DP求出 d r o o t d_{root} droot
f [ x ] f[x] f[x]表示已 x x x为根,把 x x x作为源点,从 x x x向子树出发所获得的最大流量
f [ r o o t ] = d [ r o o t ] f[root]=d[root] f[root]=d[root]
假设 f [ x ] f[x] f[x]已被求出,其子节点 y y y f [ y ] f[y] f[y]尚未被求出,那么 f [ y ] f[y] f[y]中包含两部分
\qquad 1、从 y y y流向已 y y y为根的子树的流量为 d [ y ] d[y] d[y]
\qquad 2、从 y y y沿着到父亲节点 x x x的河道,流向其他区域的流量
因为 f [ x ] f[x] f[x] x x x的总流量,那么从 x x x y y y的流量为 min ⁡ ( d [ y ] , c ( x , y ) ) \min(d[y],c(x,y)) min(d[y],c(x,y)),所以从 x x x流向 y y y以外的流量为两者之差,所以把 y y y作为源点先流到 x x x在流向其他部分的流量就是这个差再于 c ( x , y ) c(x,y) c(x,y)相比取最小值的结果。
f [ y ] = d [ y ] + { m i n ( f [ x ] − m i n ( d [ y ] , c ( x , y ) ) , c ( x , y ) ) d e g r e e [ x ] &gt; 1 c ( x , y ) d e g r e e [ x ] = 1 f[y]=d[y]+\begin{cases} min(f[x]-min(d[y],c(x,y)),c(x,y))&amp;degree[x]&gt;1\\c(x,y)&amp;degree[x]=1\end{cases} f[y]=d[y]+{min(f[x]min(d[y],c(x,y)),c(x,y))c(x,y)degree[x]>1degree[x]=1
可以用 d f s dfs dfs来求得结果
时间复杂度为 O ( n ) O(n) O(n)

code2

#include <bits/stdc++.h>
using namespace std; 
const int maxn = 2e5 + 100;
const int maxm = 4e5 + 100; 

template <typename T>
inline void read(T &s) {
    s = 0; 
    T w = 1, ch = getchar(); 
    while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
    while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
    s *= w; 
} 

template <typename T> 
inline void write(T s) {
	T w = 0, c[15]; 
	if (s < 0) putchar('-'), s = -s; 
	while(s) c[++w] = (s % 10) + 48, s /= 10;
	while(w) putchar(c[w--]); 
}

template <typename T>
inline void output(T size, T arr[maxn]) {
	for (int i = 1; i <= size; ++i) {
		printf("%d ", arr[i]); 
	}
	puts(""); 
}

inline int max(int a, int b) { return a > b ? a : b; }
inline int min(int a, int b) { return a < b ? a : b; }

int T, n, tot, cnt, ans; 
int lin[maxn], deg[maxn], snk[maxn], d[maxn], f[maxn]; 
bool vis[maxn]; 
struct node {
    int next, to, dis; 
} edge[maxm]; 

inline void add(int from, int to, int dis) {
    deg[to]++; 
    edge[++tot].to = to;
    edge[tot].dis = dis; 
    edge[tot].next = lin[from]; 
    lin[from] = tot; 
}

void dp(int x) {
	vis[x] = true; 
	d[x] = 0; 
	for (int i = lin[x]; i; i = edge[i].next) {
		int y = edge[i].to; 
		if (vis[y]) continue; 
		dp(y); 
		if (deg[y] == 1) d[x] += edge[i].dis; 
		else d[x] += min(d[y], edge[i].dis); 
	}
}

void dfs(int x) {
	vis[x] = true; 
	for (int i = lin[x]; i; i = edge[i].next) {
		int y = edge[i].to;
		if (vis[y]) continue; 
		if (deg[x] == 1) f[y] = d[y] + edge[i].dis; 
		else f[y] = d[y] + min(f[x] - min(d[y], edge[i].dis), edge[i].dis); 
		dfs(y); 
	}
}

int main() {
    read(T); 
    while (T--) {
        tot = 0;
		cnt = 0; 
		ans = -1;   
        memset(deg, 0, sizeof(deg)); 
        memset(lin, 0, sizeof(lin)); 
        
        read(n);
        for (int i = 1; i < n; ++i) {
            int x, y, z; 
            read(x), read(y), read(z); 
            add(x, y, z); 
            add(y, x, z); 
        }
		
		int root = 1; 
		memset(d, 0x3f, sizeof(d)); 
		memset(vis, false, sizeof(vis));
		dp(root);
		 
		memset(vis, false, sizeof(vis)); 
		f[root] = d[root]; 
		dfs(root);  
		
		for (int i = 1; i <= n; ++i) {
			ans = max(ans, f[i]); 
		}
		
		write(ans), putchar('\n');  
    }
    return 0; 
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值