UVALive - 4614 Moving to Nuremberg (树形DP)

题意:

找出一个点,使得从这个点出发经过规定点规定次数路径总和最短

思路:

首先先建树,设1为根,并计算从1出发的总路径

那么从父结点递推下去算其字节点有:

dp[v] = dp[u]+(sum-f[v])*len*2-f[v]*len*2 式子的意思就是v以上的点(-f[v])要多走u->v, 然后呢由于dp[u]里面含有v以下的点v->u的路径,所以减去

代码:

#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
#define INF 1000000000000000LL
const int maxn = 50010;
int n, m, num, cas, u, v;
int head[maxn], nex[maxn*2], ev[maxn*2], temp[maxn];
long long fre[maxn], f[maxn], ew[maxn*2], dp[maxn], dis[maxn];
long long w, sum, ans;

void aedge(int u, int v, long long w) {
	nex[++num] = head[u];
	head[u] = num;
	ev[num] = v;
	ew[num] = w;
}


void pre_dfs(int fa, int u) {
	dis[u] = 0, f[u] = 0;
	for(int i=head[u]; i!=-1; i=nex[i]) {
		if(ev[i] != fa) {
		pre_dfs(u, ev[i]);
		dis[u] += dis[ev[i]] + ew[i]*2*f[ev[i]];
		f[u] += f[ev[i]];//起始点累积所有的次数
		}
	}
	f[u] += fre[u];
}
void dfs(int fa, int u, long long dis) {
	dp[u] = dis;
	if(dp[u] < ans) ans = dp[u];
	for(int i = head[u]; i!=-1; i=nex[i]) {
		if(fa != ev[i]) 
			dfs(u, ev[i], dis+2*ew[i]*(sum-2*f[ev[i]]));
	}
}

      

void deal() {
	pre_dfs(1, 1);
	ans = INF;
	dfs(1, 1, dis[1]);
	printf("%lld\n", ans);
	int cnt = -1;
	for(int i=1; i<=n; i++) {
		if(dp[i] == ans) 
			temp[++cnt] = i;
	}
	for(int i=0; i<=cnt; i++) {
		if(i != cnt) printf("%d ", temp[i]);
		else printf("%d\n", temp[i]);
	}
}	
int main() {
	scanf("%d", &cas);
	while(cas--) {

		memset(head, -1, sizeof(head));
		memset(fre, 0, sizeof(fre));
		num = -1;
		scanf("%d", &n);
		for(int i=0; i<n-1; i++) {
			scanf("%d%d%lld", &u, &v, &w);
			aedge(u, v, w);
			aedge(v, u, w);
		}
		sum = 0;
		scanf("%d", &m);
		for(int i=0; i<m; i++) {
			scanf("%d%lld", &u, &w);
			fre[u] = w;
			sum += fre[u];
		}
		deal();
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值