大意:树上的点可以买卖,从任意一点买入,再从任意一点卖出。求最大差价。
算法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;
}