Acwing287.积蓄程度 树形dp换根

news/2024/6/16 11:49:52 标签: 动态规划, 算法

link
二次扫描与换根,对于没有确定根的题目往往可以用到,可以减少一个枚举根的复杂度

题意

在这里插入图片描述

思路

1.考虑朴素解法,枚举根 s s s ,设 d[x] 为从 x 出发流向子树的最大流量,很容易求出dp方程(即代码中dfs部分)。对于每个根都进行一次dp,时间复杂度 O ( n 2 ) O(n^2) O(n2)
2.考虑优化,首先任选一个源点 s s s 进行同上的dp,设 f[x] 表示把 x 作为根,流向整个水系的最大流量。则对于根节点 x x x , f[x] = d[x]。考虑 x x x 的子节点 y y yf[y] 包涵两部分:

  1. y y y 流向以 y y y 为根的子树的流量,即为 d[y]
  2. y y y 沿着父节点 x x x 的河道,流向水系中其他部分的流量,设为ad

容易得到 f[y] = f[x] + ad,那么怎么求 ad 呢?
把 x 作为根节点的总流量为 f[x], 从 x 流向 y 的流量为 min(d[y], w[x,y]) ,其中 w[x,y] 表示 xy 两点间河道容量。那么 x 流向除 y 以外的其他部分流量就是二者之差,于是把y作为根,先流到 x ,再流向其他部分的流量就是这个差与 w[x,y] 取最小值的结果。(前提是 x , y x, y x,y 度数均大于1)

if(in[cur] != 1 && in[to] != 1)
	ad = min(f[cur] - min(e[i].dis, d[to]), e[i].dis);
else if(in[cur] != 1 && in[to] == 1) 
	ad = min(f[cur] - e[i].dis, e[i].dis);
else
	ad = e[i].dis;

注意 dfs2(to, cur) 的搜索要放到更新完 to 之后。

代码

int n;
int head[maxn], cnt;
struct Edge {
	int to, dis, next;
}e[maxn * 2];
bool vis[maxn];
int d[maxn], f[maxn];
int in[maxn];
void add(int u, int v, int w) {
	++cnt;
	e[cnt].to = v;
	e[cnt].dis = w;
	e[cnt].next = head[u];
	head[u] = cnt;
}
int ans;
void dfs(int cur = 1, int fa = 0) {
	for(int i = head[cur]; i; i = e[i].next) {
		int to = e[i].to;
		if(to == fa) continue;
		dfs(to, cur);
		if(in[to] == 1) {
			d[cur] += e[i].dis;
		}
		else {
			d[cur] += min(e[i].dis, d[to]);
		}
	}
}
void dfs2(int cur = 1, int fa = 0) {
	for(int i = head[cur]; i; i = e[i].next) {
		int to = e[i].to;
		if(to == fa) continue;
		f[to] = d[to];
		int ad;
		if(in[cur] != 1 && in[to] != 1)
			ad = min(f[cur] - min(e[i].dis, d[to]), e[i].dis);
		else if(in[cur] != 1 && in[to] == 1) 
			ad = min(f[cur] - e[i].dis, e[i].dis);
		else
			ad = e[i].dis;
		f[to] += ad;
		ans = max(ans, f[to]);
		dfs2(to, cur);
	}
}
void solve() {
	cnt = 0;
	ans = 0;
	memset(f, 0, sizeof(f));
	memset(d, 0, sizeof(d));
	memset(in, 0, sizeof(in));
	memset(head, 0, sizeof(head));
	cin >> n;
	for(int i = 1; i < n; i++) {
		int u, v, w;
		cin >> u >> v >> w;
		in[u]++;
		in[v]++;
		add(u, v, w);
		add(v, u, w);
	}
	dfs(1, 0);
	f[1] = d[1];
	ans = f[1];
	dfs2(1, 0);
	cout << ans << endl;
}

http://www.niftyadmin.cn/n/1764828.html

相关文章

(第二天)原型、继承

原型 引入 我们依然定义一个Person类 1 function person(age,name){ 2 this.age age; 3 this.name name; 4 this.information function(){ 5 return "年龄" this.age",""名字" this.name; 6 } 7 } 下面我…

去掉redhat linux提示注册

去掉提示注册的话&#xff0c;卸载几个软件包&#xff1a;#rpm -qa | grep subscription-manager 然后移除那出现的几项吧&#xff1a;#yum remove subscription-manager-gnome #yum remove subscription-manager-firstboot #yum remove subscription-manager 顺带在去…

昆明站打铁记

Place229&#xff0c;差一发罚时铁了。 热身赛 热身赛属于是伏笔了&#xff0c;3道题都很签到&#xff0c;前两题都没啥可说的&#xff0c;第3题我打完提交&#xff0c;准备收拾东西下班发现wa掉了&#xff0c;看了5分钟也没看出来有啥毛病&#xff0c;然后队友看到输出格式里…

(回溯法)数组中和为S的N个数

Given a list of numbers, find the number of tuples of size N that add to S. for example in the list (10,5,-1,3,4,-6), the tuple of size 4 (-1,3,4,-6) adds to 0. 题目&#xff1a; 给一数组&#xff0c;求数组中和为S的N个数 思路&#xff1a; 回溯法&#xff0c;数…

CF1659D. Reverse Sort Sum*

link 思维 题意 A为一个长度为 nnn &#xff0c;仅由0&#xff0c;1组成的数组&#xff0c;设 BiB_iBi​ 为把 AAA 的前 iii 项从小到大排序&#xff0c;其余项不变得到的数组。设数组 CΣi1nBiC \Sigma_{i1}^n B_iCΣi1n​Bi​ 例如A[0,1,0,1]&#xff0c;则B1[0,1,0,1],B2…

CF1668D. Optimal Partition* dp 2100

link #782 div.2 2100&#xff0c; 树状数组优化dp&#xff0c;离散化 题意 给你一个数组&#xff0c;要求你分成若干个连续的子数组&#xff0c;每个子数组的价值为&#xff1a; 若子数组数字和为正数&#xff0c;价值为数组长度和为0&#xff0c;价值为0和为负数&#xff…

20150802

Dont forget to say greetings to Uncle Wang.Hows it going? Great . Cool.Take care.See you later.Alligator.How are you getting along?Whats up? Nothing much.Whats happening? Same as usual.How have you been?Thank you million.Not bad.alligatorcrocodilesubs…

F. 孤独的树 思维

link 思维&#xff0c; 正解是思路比较清晰但写起来还挺复杂的树形dp&#xff0c;有空补一下。 牛客的小白赛&#xff0c;官方视频题解 题意 思路 乱搞的&#xff0c;&#xff0c;其实并不知道为什么对&#xff0c;但确实过了这道题。 先预处理出 c[i] 数组&#xff0c;表示 …