积蓄程度<原创解法>

有一个树形的水系,由 N−1 条河道和 N 个交叉点组成。

我们可以把交叉点看作树中的节点,编号为 1∼N,河道则看作树中的无向边。

每条河道都有一个容量,连接 x 与 y 的河道的容量记为 c(x,y)。

河道中单位时间流过的水量不能超过河道的容量。

有一个节点是整个水系的发源地,可以源源不断地流出水,我们称之为源点。

除了源点之外,树中所有度数为 1 的节点都是入海口,可以吸收无限多的水,我们称之为汇点。

也就是说,水系中的水从源点出发,沿着每条河道,最终流向各个汇点。

在整个水系稳定时,每条河道中的水都以单位时间固定的水量流向固定的方向。

除源点和汇点之外,其余各点不贮存水,也就是流入该点的河道水量之和等于从该点流出的河道水量之和。

整个水系的流量就定义为源点单位时间发出的水量。

在流量不超过河道容量的前提下,求哪个点作为源点时,整个水系的流量最大,输出这个最大值。

输入格式

输入第一行包含整数 T,表示共有 T 组测试数据。

每组测试数据,第一行包含整数 N。

接下来 N−1 行,每行包含三个整数 x,y,z,表示 x,y 之间存在河道,且河道容量为 z。

节点编号从 1 开始。

输出格式

每组数据输出一个结果,每个结果占一行。

数据保证结果不超过 2e9。

数据范围

N≤2×10^5

输入样例:

1
5
1 3 2
1 2 4
2 5 3
2 4 2

输出样例:

7

我们先把样例图放出来

怎么思考这个题
仍然是不能把每个节点当做父节点去做的
那么我们就考虑能不能用两边dfs得出答案,就和上一道题一样的思路
我们先用一遍dfs求出每个点到它下面的点的流量大小
但是注意了,这有一点不一样,就是我们保存的不是和,而是每层和的最小值
但是我们之前做的题思路就是直接去搜一条路,直至搜尽为止
其实是一样的,以2点为例
2点可以到4点和5点,那么2点得到的流量就是到4和5的流量之和,自然为5
那么就要用2点去更新1点,但是2点到1点的流量是4,所以我们就要在其中选取最小值
所以我们怎么更新?首先就是要在父亲节点到子节点的流量w[i]和子节点到其下面节点的流量之和中取min
也就是flu[u] = min(flu[u],dfs(j,u,))
但是刚开始的时候要把flu[u]设为w[i]
这样我们可以得到每个点以下的流量
但是不要忘了,我们还要求出每个点上面的流量
也就是说把一个固定点当做源点的话它是可以往各个方向输送水源的
那么我们就要考虑了,怎么得到每个点到上面点的流量?
还是按照上一题的思路去考虑,我们设法把从上而下dfs得到的值给用上
我们先看1点,1点到2点的流量是4,而1点到其他点的流量总和是2,
所以我们要取其中的最小值,就是子节点与父节点连通的流量w[i],要和其父节点除了w[i]外其他点的流量总和取一个最小值
所以这就很明白了
我们首先就要维护每个点的流量总和(这不就是设问吗==)
更新顺序果然还是由上而下呀
这个是肯定的,子节点需要用到父节点的信息
所以我们需要从下到上去更新
接下来我们分析分析特殊情况
1.若我们没有对叶子结点做特殊处理,那么叶子结点返回的值一定是0,因为就没法进入for循环嘛
所以这肯定不行啊
而且我们观察到最小的边的权值就一定是1
那么就暗示我们只要出现流量为0的点,那么它一定就是叶子结点
由于叶子结点的流量是正无穷,所以我们直接返回0x3f3f3f3f就可以了
2.若是主干道就只有一条,意思是说我们在做dfs_up由上到下顺序更新结点总流量值的时候
其中有一句话是这样的:sum[j] = min(w[i],sum[u] - w[i])
很明显我们是没法更新这个结点的,因为最小值就是0
那么我们还要加上一个特判:若是w[i]==sum[u],那么我们随便选一个加上去就行

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 400010;
int t,n;
int e[N],ne[N],h[N],w[N],idx;
int sum[N];
int maxx = -2e9;
void add(int a,int b,int c)
{
	e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}
int dfs_down(int u,int fa)
{
	sum[u] = 0;
	for(int i=h[u];~i;i=ne[i])
	{
		int j = e[i];
		if(j==fa) continue;
		int flu = w[i];
		flu = min(flu,dfs_down(j,u));
		sum[u] += flu;
	}
	if(!sum[u]) return 0x3f3f3f3f;
	else return sum[u];
}
void dfs_up(int u,int fa)
{
	for(int i=h[u];~i;i=ne[i])
	{
		int j = e[i];
		if(j==fa) continue;
		if(sum[u]!=w[i])
		sum[j] += min(w[i],sum[u]-w[i]);
		else sum[j] += w[i];
		dfs_up(j,u);
	}
}
int main()
{
	cin>>t;
	while(t--)
	{
		idx = 0;
		memset(sum,0,sizeof sum);
		memset(h,-1,sizeof h);
		maxx = -2e9;
		cin>>n;
		for(int i=1;i<=n-1;i++)
		{
			int a,b,c;
			cin>>a>>b>>c;
			add(a,b,c),add(b,a,c);
		}
		
		dfs_down(1,-1);
		dfs_up(1,-1);
	
		for(int i=1;i<=n;i++)
		maxx = max(maxx,sum[i]);
		cout<<maxx<<endl;
	}
	return 0;
} 

要加油哇!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值