HDU - 6201:transaction transaction transaction

大意:树上的点可以买卖,从任意一点买入,再从任意一点卖出。求最大差价。

算法1:最长路。

             新构建两个点,作为源点S和汇点T,这里可以另S=0,T=N+1。然后把1-N中的每一个点与S建立一条边,边权为-w[i](点权),代表买入。再把1-N中的每一个点与T建立一条边,边权为w[i],代表卖出。

              然后利用SPFA算法,求S—T的最长路经。

             注意这里边的数量达到了4*n个。

#include<cstdio>
#define MAXN 1e9 + 7
int n, m, T, k;
int q[410000], point[410000], next[410000], first[410000], d[420000], w[420000], w1[410000];
bool v[410000];
void add(int x, int y, int z){
    point[++k] = y; next[k] = first[x]; first[x] = k; w1[k] = z;
}
void spfa(){
    int no, ne;
    int l = 0, r = 0;
    for (int i = 0; i <= n + 1; i++) d[i] = -MAXN;
    d[0] = 0;q[++r] = 0;v[0] = 1;
    while(l < r){
        l++;
        no = q[l];
        v[no] = 0;
        for (int i = first[no]; i != 0; i = next[i]){
            ne = point[i];
            if(d[ne] < d[no] + w1[i]){
                d[ne] = d[no] + w1[i];
                if (!v[ne]){
                    q[++r] = ne;
                    v[ne] = 1;
                }
            }
        }
    }
}
void read(int &x){
    x=0; char c=getchar();
    while(c>'9'||c<'0') c=getchar();
    while(c<='9'&&c>='0') x=x*10+c-'0',c=getchar();
}
int main(){
    scanf("%d", &T);
    while(T--){
        k = 0;
        read(n);
        for (int i = 1; i <= n; i++) read(w[i]);
        for (int i = 0; i <= n + 1; i++) first[i] = v[i] = 0;
        for (int i = 1; i < n; i++){
            int x, y, z;
            read(x); read(y); read(z);
            add(x, y, -z);
            add(y, x, -z);
        }
        for (int i = 1; i <= n; i++){
            add(0, i, -w[i]);
            add(i, n + 1, w[i]);
        }
        spfa();
        printf("%d\n", d[n + 1]);
    }
    return 0;
}

算法2:树形DP

              对于每个点我们考虑经过这个点的最大卖出和最小买入。最大卖出和最小买入可能在该点子树内,也可能是该点。但不可能同时是该点。

              所以定义数组d[x][0/1]。

              d[x][0]代表经过这个点的最小买入;d[x][1]代表经过该点的最大卖出。

              我们先给其赋初始值,d[x][0]=-w[i],d[x][1]=w[i];

              在枚举其子结点时该如何转移呢?

              d[x][0] = max(d[x][0], d[y][0]-w[i]);//y是x的子节点

              d[x][1] = max(d[x][1], d[x][1]-w[i]);

             需要注意的是d[x][0]在这里是负数,所以我们需要求其最大值,这才是最小买入。

              ans在递归过程中,当某个结点的子节点全部枚举完毕后更新。ans = max(ans, d[x][0] + d[x][1]);

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int T, n, m, k, ans;
int point[200010], _next[200010], first[200010], w[200010], w1[100010];
int d[100010][2];
bool v[100010];
inline int max(int x, int y){
    return x > y ? x : y;
}
void dfs(int x){
	v[x] = 1;
	d[x][0] = -w1[x]; d[x][1] = w1[x];
	for (int i = first[x]; i != 0; i = _next[i]){
		if (v[point[i]]) continue;
		dfs(point[i]);
		d[x][0] = max(d[point[i]][0] - w[i], d[x][0]);
		d[x][1] = max(d[point[i]][1] - w[i], d[x][1]);
	}
	ans = max(ans, d[x][0] + d[x][1]);
}
int main(){
	scanf("%d", &T);
	while(T--){
		ans = k = 0;
        scanf("%d", &n);
		for (int i = 1; i <= n; i++) scanf("%d", &w1[i]), first[i] = v[i] = 0;
		for (int i = 1; i < n; i++){
			int x, y, z;
			scanf("%d%d%d", &x, &y, &z);
			point[++k] = y; _next[k] = first[x]; first[x] = k; w[k] = z;
			point[++k] = x; _next[k] = first[y]; first[y] = k; w[k] = z;
		} 
		dfs(1);
		printf("%d\n", ans);
    }
    return 0;
}

              

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值