transaction transaction transaction HDU - 6201 (树形DP)

transaction transaction transaction

 题目链接:HDU - 6201 

题意:一个商人得知《the Man Who Changed China》此书很火,决定在一座城市批发一本书(你没有看错,就是一本),带到另一座城市卖,赚取差价,但是由一座城市到另一座城市要花钱打车(打车费用是1元/km);已知共有n座城市,由n-1条路连接,交通网是一棵树;已知此书在每座城市的价格,以及每条路的长度,问买了一本书再卖出后商人最多盈利多少?(商人可以以任意城市为起点);

思路:网上有种二维DP解法硬是没看懂,还是写我的一维加两次dfs解法吧~~

对于每一个节点来说多可能买书卖书,这里我们把u节点看作是在这里卖书,在其他节点买书,也就是说u是终点;问题来了,商人是从u的son节点来的还是u的father节点来的?必是其中一种情况,从son节点来的记为donw,从father节点来的记为up,那么商人最终的盈利为val-min(down, up);

现在的问题是求up和down;

down最好求了,一遍bfs,down[u]=min(val[son]+w,  down[son]+w) (w表示u到son的花费)

up就有点绕了;;;

 

如上图,蓝色路径是down[u], 紫色路径是up[fa], 由图可知,up[fa]+w并不是up[u],而w+红色路径才是up[u],所以说求up与down并不同,因为up[u]的路线可能是经由u的father,再经由father的father,也可能是经由father后,再经由father的son即u的brother;up[u]=min(up[fa], down[fa])???   如果down[fa]未经由u当然这样了,但上图可知,红色路径并非是down,蓝色路径才是,红色路径只是次短路,,所以还要记录u是否在up[fa]的最短路径上,同时还要求出fa的此短路,现在就明确了:

up[u]=min(val[fa]+w, up[fa]+w, down[fa]+w);注意此式的一个条件u不在down[fa]上,若u在down[fa]上,则将down[fa]改为此短down[fa];

详情请看代码:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=1e5+10;
int up[maxn], down[maxn][2];//down[i][0]表示在i的子节点买书到i的最小花费,down[i][1]表示次小花费;
int path[maxn];//path[u]=v表示u到子节点的最短路经由v;
int val[maxn];
struct node{
	int v, w, nxt;
	node(){};
	node(int v0, int w0, int n){
		v=v0, w=w0, nxt=n;
	} 
}edge[maxn<<1]; 
int head[maxn], cnt;
void add(int u, int v, int w){
	edge[cnt]=node(v, w, head[u]);
	head[u]=cnt++;
}
void dfs_down(int u, int fa){
	down[u][0]=down[u][1]=INF;
	for(int i=head[u]; i!=-1; i=edge[i].nxt){
		int v=edge[i].v, w=edge[i].w;
		if(v==fa) continue;
		dfs_down(v, u);
		int temp1, temp2;//temp1表示最短路,temp2表示此短路;
		if(val[v]+w<down[v][0]+w) temp1=val[v]+w, temp2=down[v][0]+w;
		else temp1=down[v][0]+w, temp2=min(val[v]+w, down[v][1]+w);
		if(temp1<down[u][0]){
			down[u][1]=down[u][0];
			down[u][0]=temp1;
			path[u]=v;
		}
		else down[u][1]=min(down[u][1], temp1);
	}
}
void dfs_up(int u, int fa){
	for(int i=head[u]; i!=-1; i=edge[i].nxt){
		int v=edge[i].v, w=edge[i].w;
		if(v==fa) continue;
		up[v]=val[u]+w;
		if(path[u]==v){
			up[v]=min(up[v], min(up[u]+w, down[u][1]+w));
		}
		else{
			up[v]=min(up[v], min(up[u]+w, down[u][0]+w));
		}
		dfs_up(v, u);
	}
}
int main(){
	int T;
	scanf("%d", &T);
	while(T--){
		int n;
		scanf("%d", &n);
		for(int i=1; i<=n; i++){
			scanf("%d", &val[i]);
		}
		cnt=0;
		memset(head, -1, sizeof(head));
		for(int i=1; i<n; i++){
			int x, y, z;
			scanf("%d%d%d", &x, &y, &z);
			add(x, y, z);
			add(y, x, z);
		}
		dfs_down(1, 1);
		up[1]=INF;//把1看作根节点,所以1没有father,up[1]=INF;
		dfs_up(1, 1);
		int ans=-INF;
		for(int i=1; i<=n; i++){
			ans=max(ans, val[i]-min(down[i][0], up[i]));
		} 
		printf("%d\n", ans);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值