题意
给出一颗有n个点的树,共有n - 1 条边,树的根节点是1,每两个点之间的一条边都有一个权值。现在可以从点a直接跳到点b只花费p,且|da - db| = k,即点a和点b之间的深度差为k。问从点u到点v的最小花费是多少?
分析
可以理解为在深度差为k的两层点之间各自连了一条权值为p的边,如下图:
假设这两层点之间的高度差为k,如果要直接在这两层点的任意两个点之间连一条边的话,这显然是不现实的。我们可以在这两层点之间增加两个点,如下图:
然后添加边:
其中蓝色的边权为0,橙色的边权为p,这样一来,每个点最多会添加四条边
听知乎博主严格鸽说用vector会爆内存,所以我就直接用链式前向星了
解题一共分为以下几步:
- 根据题目输入的n - 1条边建图
- dfs求出每个点的深度
- 对每个点都增加四条边(可能会少于四条边)
- 利用堆优化版dijstra求点u到点v的最小距离
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const ll N = 4e6 + 7;
ll t,n,from,to,k,p,idx = 0;
ll h[N],d[N],maxdep;
ll dis[N];
bool vis[N];
//注意要用long long!!!我这里为了方便就全都用long long了
struct edge{
ll to,ne,w;//这条边的指向,权值
}e[N];
inline ll read()
{
char ch = getchar();
ll x = 0,f = 1;
while(ch < '0' || ch > '9'){
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
void add(ll x,ll y,ll w)
{
e[idx].to = y;
e[idx].w = w;
e[idx].ne = h[x];
h[x] = idx ++;
}
void dfs(ll u,ll fa)
{
d[u] = d[fa] + 1;
maxdep = max(maxdep,d[u]);
for(ll i = h[u];~i;i = e[i].ne){
if(e[i].to == fa) continue;
dfs(e[i].to,u);
}
}
void dij(ll u)
{
memset(dis,0x3f,sizeof dis);
memset(vis,false,sizeof vis);
priority_queue<PII,vector<PII>,greater<PII>> Q;
dis[u] = 0;
Q.push({0,u});
while(Q.size()){
PII top = Q.top();
Q.pop();
ll x = top.second;
if(vis[x]) continue;
vis[x] = true;
for(ll i = h[x];~i;i = e[i].ne){
ll j = e[i].to;
if(dis[j] > dis[x] + e[i].w){
dis[j] = dis[x] + e[i].w;
Q.push({dis[j],j});
}
}
}
}
int main()
{
t = read();
while(t --){
n = read();
idx = 0, maxdep = 0,d[0] = -1,d[1] = 0;
memset(h,-1,sizeof h);
for(ll i = 1;i < n;i ++){
ll x = read(),y = read(),w = read();
add(x,y,w);//无向边
add(y,x,w);
}
k = read(),p = read(),from = read(),to = read();
dfs(1,0);
//加点
for(ll i = 1;i <= n;i ++){
if(d[i] != 0){
add(n + 2 * d[i] - 1, i, p);//图四中的边1
}
if(d[i] != maxdep){
add(n + 2 * (d[i] + 1), i, p);//图四中的边3
}
if(d[i] + k <= maxdep){
add(i, n + 2 * (d[i] + k) - 1, 0);//图四中的边4
}
if(d[i] - k >= 0){
add(i, n + 2 * (d[i] - k + 1), 0);//图四中的边2
}
}
dij(from);
cout << dis[to] << '\n';
}
return 0;
}
如果有不理解的地方大家可以发在评论区一起讨论喔~