“卓见杯”第五届CCPC中国大学生程序设计竞赛河南省赛网络赛 D Defending Plan Support(树形dp,贡献值,递推)

本文介绍了一种使用树形动态规划方法解决特定图论问题的算法,即在一个带有边权和节点权重的树状结构中,寻找一个节点作为基点,使得该节点到所有其他节点距离与其权重乘积之和最小。文章详细阐述了解题思路和代码实现,通过两次深度优先搜索完成计算。

题目描述

The architectural structure of the college is strange, but the rule is that there is only one simple path between every two classrooms. Now the battle between Class A and Class F broke out. As a support staff of Class F, you have to go to every fight in time to help out. With the help of icebound, Class F know the probability of each classroom being attacked. So we define an important degree for each classroom. Now ask you to find a classroom x in n classrooms as your resting base which has the minimum F(x)F(x) = ∑ w(i) × d(x, i)d is the distance between x and i.

输入

he first line is an integer n,which is the number of classrooms.
Then n-1 lines follow. Each line has three numbers x,y,z. There is a road of meters between x and y.
The last line contains n numbers. The i‑th number w(i) is the important degree of the classroom.
2 ≤ n ≤ 5 × 105, 0 ≤ z, wi ≤ 1000,1 ≤ x, y ≤ n

输出

Output a line with an integer, representing the minimum F(x).

样例输入 Copy

5
1 2 1
2 3 1
2 4 1
3 5 6
2 3 1 8 7

样例输出 Copy

60

题意:给出一个树,有边权,有节点的重量,让你求g[x]的最小值,g[x] = ∑ ( w[i] * d(i,x) ) 。w[i] 为除x外其他一些节点重量,d(i,x)  为 i 到 x 的距离。

思路:说实话,比赛时,不会写,队友强,用树形dp,递推 贡献值。

树形dp. 首先随机选取一个根, 然后预处理出f[i]表示i的子树上所有点的权值和, g[i]表示i的整颗子树对当前i的贡献

转移 f[i]=w[i]+∑j是i的儿子 f[j]f[i]=w[i]+∑j是i的儿子 f[j] , g[i]=∑j是i的儿子 (g[j]+f[j]∗dis(i,j))g[i]=∑j是i的儿子 (g[j]+f[j]∗dis(i,j))

其中 w[i] 表示i点的权值

然后从根再做一次dfs, 不断将父亲节点当做子节点的子树并将贡献向下传递给子节点即可

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long
#define Max 500010 
int w[Max],f[Max];
bool book[Max]; 
ll g[Max];
struct node{
	int y,z;
};
vector<node> v[Max];

int dfs(int u)
{
	book[u] = true;
	int sum = w[u];
	for(int i = 0;i<v[u].size();i++){
		node tt = v[u][i];
		int v = tt.y;
		if(!book[v]){
			int k = dfs(v);
			sum += k;
			g[u] += g[v] + (ll) k * tt.z; 
		}
	}
	return f[u] = sum;
}

ll dfs1(int u,int fw,ll fg)  // 节点u,fw为当初以1为根节点的树,u为子树时,除u为子树的结点重量和之外的所有结点重量之和 
							// fg 为 fw 对 节点u的贡献值。 
{
	ll Ma = g[u]+fg;
	book[u] = true;
	for(int i = 0;i<v[u].size();i++){
		node tt = v[u][i];
		int v = tt.y;
		if(!book[v]){
			Ma = min(Ma,dfs1(v,fw + f[u]-f[v],fg + g[u]-g[v]-(ll)f[v]*tt.z +(ll)(fw+f[u]-f[v])*tt.z));
		}  // fg + g[u]-g[v]-(ll)(f[v]*tt.z) 为当初以1为根节点时,v为根节点的子树 时,除v为根节点的子树和 u本身节点外,所有节点对
			// 所有节点对u的贡献值;(fw+f[u]-f[v])*tt.z))  (fw+f[u]-f[v])为除 v 为根节点子树外的所有权值。乘于tt.z 只是这一段的权重。 
	}   // 就是运用的 g[i]的递推公式。这个是从根节点往下扫一边,dfs1主要是为了求除当前节点为子树外的所有节点,对当前节点的贡献值。 
	return Ma;
}
int main()
{
	int n;
	int x,y,z;
	scanf("%d",&n);
	for(int i = 1;i<n;i++){
		scanf("%d%d%d",&x,&y,&z);
		//G[u].push_back(v*1005+w);   如果爆内存了,小技巧,可以这样压缩一下,看数据范围压缩。 
        //G[v].push_back(u*1005+w);   或者把w[] 和 f[]合为一个数组。 
		node tt;
		tt.y = y;
		tt.z = z;
		v[x].push_back(tt);
		tt.y = x;
		v[y].push_back(tt);
	}
	for(int i = 1;i<=n;i++)
		scanf("%d",&w[i]);
	dfs(1);	
	memset(book,false,(n+1)*sizeof(bool));
	printf("%llu \n",dfs1(1,0,0));        
	return 0;
} 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值