题意
n 个城市构成一棵树,所有城市中都流通一种商品,但价格不同。一个商人从一个城市到另一个城市。在路上,他可以在一个城市中买一件商品,然后在这座城市后面的一座城市中卖掉,并赚取差价。给出起点和终点,问商人能得到的最大利润是多少?
思路
LCA + dp
原题解作者的链接
http://www.hankcs.com/program/algorithm/poj-3728-the-merchant.html
假设有路径 u … t … v,则 u 到 t 的最利润有三种可能:u 到 t 的最大利润、t 到 v 的最大利润、u 到 t 买, t 到 v 卖,即 t 到 v 的最大价格 - u 到 t 的最小价格。
详见代码。
链接
http://poj.org/problem?id=3728
代码
#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int maxv = 5e4 + 10;
const int maxlogv = 16;
const int inf = 0x3f3f3f3f;
int n, q;
//输入
//树
vector<int> G[maxv];
//价格
int price[maxv];
//lca零件
//v向上走2^k步到达的祖先
int parent[maxlogv][maxv];
//v的深度
int depth[maxv];
//dp
//点v向上走2^k步中的最高价格 点v向上走2^k步中的最低价格
int dp_max[maxlogv][maxv], dp_min[maxlogv][maxv];
//点v向上走2^k步中的最大利润 点v向下走2^k步中的最大利润
int dp_up[maxlogv][maxv], dp_down[maxlogv][maxv];
//dfs初始化 父亲节点(走2^0步到达的祖先),深度,4个dp数组
//当前节点,父亲节点,深度
void dfs(int v, int p, int d){
parent[0][v] = p;
depth[v] = d;
dp_up[0][v] = max(price[p] - price[v], 0);
dp_down[0][v] = max(price[v] - price[p], 0);
dp_max[0][v] = max(price[v], price[p]);
dp_min[0][v] = min(price[v], price[p]);
for(int i = 0; i < G[v].size(); i++){
if(G[v][i] != p) dfs(G[v][i], v, d + 1);
}
}
//初始化倍增lca数组
void init(int V){
memset(dp_max, 0, sizeof dp_max);
memset(dp_min, inf, sizeof dp_min);
dfs(0, -1, 0);
for(int k = 0; k + 1 < maxlogv; k++){
for(int v = 0; v < V; v++){
if(parent[k][v] < 0) parent[k + 1][v] = -1;
else{
parent[k + 1][v] = parent[k][parent[k][v]];
int t = parent[k][v];
dp_max[k + 1][v] = max(dp_max[k][v], dp_max[k][t]);
dp_min[k + 1][v] = min(dp_min[k][v], dp_min[k][t]);
dp_up[k + 1][v] = max(max(dp_up[k][v], dp_up[k][t]), dp_max[k][t] - dp_min[k][v]);
dp_down[k + 1][v] = max(max(dp_down[k][v], dp_down[k][t]), dp_max[k][v] - dp_min[k][t]);
}
}
}
}
//二分法求lca
int lca(int u, int v){
if(depth[u] > depth[v]) swap(u, v);
for(int k = 0; k < maxlogv; k++){
if((depth[v] - depth[u]) >> k & 1){
v = parent[k][v];
}
}
if(u == v) return u;
for(int k = maxlogv - 1; k >= 0; k--){
if(parent[k][u] != parent[k][v]){
u = parent[k][u];
v = parent[k][v];
}
}
return parent[0][u];
}
//向上走获得的最大利润
//出发节点,与lca的深度差,路上遇到的最小价格
int up(int x, int k, int &min_price){
min_price = inf;
int max_profit = 0;
int prev_min_price = inf;
for(int i = maxlogv - 1; i >= 0; i--){
if(k >> i & 1){
min_price = min(min_price, dp_min[i][x]);
max_profit = max(max_profit, dp_up[i][x]);
max_profit = max(max_profit, dp_max[i][x] - prev_min_price);
prev_min_price = min(prev_min_price, dp_min[i][x]);
x = parent[i][x];
}
}
return max_profit;
}
//向下走的最大利润
//出发节点,与lca的深度差,路上遇到的最大价格
int down(int x, int k, int &max_price){
max_price = 0;
int max_profit = 0;
int pre_max_price = 0;
for(int i = maxlogv - 1; i >= 0; i--){
if(k >> i & 1){
max_price = max(max_price, dp_max[i][x]);
max_profit = max(max_profit, dp_down[i][x]);
max_profit = max(max_profit, pre_max_price - dp_min[i][x]);
pre_max_price = max(pre_max_price, dp_max[i][x]);
x = parent[i][x];
}
}
return max_profit;
}
int main(){
scanf("%d", &n);
for(int i = 0; i < n; i++){
scanf("%d", price + i);
}
for(int i = 0; i < n - 1; i++){
int u, v;
scanf("%d %d", &u, &v);
u--, v--;
G[u].push_back(v);
G[v].push_back(u);
}
init(n);
scanf("%d", &q);
while(q--){
int u, v;
scanf("%d %d", &u, &v);
u--, v--;
int lv = lca(u, v);
int max_price, min_price;
int up_profit = up(u, depth[u] - depth[lv], min_price);
int down_profit = down(v, depth[v] - depth[lv], max_price);
int ans = max(max(up_profit, down_profit), max_price - min_price);
printf("%d\n", ans);
}
return 0;
}