k短路是问你从 s s s 到 t t t 的第 k k k 短路。
第一种做法是用
A
∗
A^{*}
A∗ 算法来做,先跑一遍 Dijkstra,接着用
A
∗
A^*
A∗ 跑一边来优化。有个性质就是这个点第
k
k
k 次从队列中出来那么这个就是它的第
k
k
k 短路了。特判如果
s
=
=
t
s==t
s==t 的话,
k
k
k 要加一。
具体看代码
#include <bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
const int N = 1005;
struct node{
int v,w;
};
int n,m,k,a,b;
int dist[N];
vector<node>road[N],rroad[N];//road正rroad反
bool dij()//先跑一遍最短路
{
priority_queue<pii,vector<pii>,greater<pii> >q;
memset(dist,0x3f,sizeof(dist));
dist[b] = 0;
q.push((pii){0,b});
while(!q.empty())
{
int u = q.top().second;
int len = q.top().first;
q.pop();
if(len != dist[u])
{
continue;
}
for(int i = 0;i < rroad[u].size();i++)
{
int v = rroad[u][i].v;
int l = rroad[u][i].w;
if(dist[v] > dist[u] + l)
{
dist[v] = dist[u] + l;
q.push((pii){dist[v],v});
}
}
}
return dist[0] != dist[a];
}
int f[N];
void write(int x)
{
cout << x << endl;
if(x != a)
{
write(f[x]);
}
printf("%d",x);
if(x != b)
{
printf("-");
}
}
struct point{
int first,second;
vector<int>t;
bool operator<(const point &x)const{
return first + dist[second] > x.first + dist[x.second];
}
};
void astar()//在跑一遍最短路
{
priority_queue<point>q;
vector<int>t;
t.push_back(0);
q.push((point){0,a,t});
while(!q.empty())
{
int u = q.top().second;
int len = q.top().first;
t = q.top().t;
q.pop();
if(u == b)
{
k--;
cout << len << endl;
if(k == 0)
{
return;
}
}
for(int i = 0;i < road[u].size();i++)
{
int v = road[u][i].v;
int l = road[u][i].w;
vector<int>tt = t;
tt.push_back(v);
q.push((point){l + len,v,tt});
}
}
while(k--)
{
cout << -1 << endl;
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
a = n;
b = 1;
while(m--)
{
int u,v,l;
scanf("%d%d%d",&u,&v,&l);
road[u].push_back((node){v,l});
rroad[v].push_back((node){u,l});
}
if(!dij())
{
printf("NO\n");
return 0;
}
if(a == b)
{
k++;
}
astar();
return 0;
}
模板题:P2901 [USACO08MAR] Cow Jogging G
第二种做法是使用最短路和可持久化合并堆来优化。
我们设是求从 s s s 到 t t t 的 k k k 短路。我们先建一个反图,接着跑一遍从 t t t 开始的最短路,建立以点 t t t 为根的最短路树,对于每一条非树边(它是从点 u u u 到点 v v v,权值为 w w w),我们可以得出它的新的贡献为 d i s t v + w − d i s t u dist_v + w - dist_u distv+w−distu,把这条边的新的权值推到根为 r o o t u root_u rootu 的可持久化堆中,接着我们建立一个小根堆。一开始我们在小根堆中推入 t r r o o t u tr_{root_u} trrootu,每次替换都有两种手段。
- 更换成同样是以 u u u 开始的非树边,注意是要严格大于当前边的新的贡献值
- 推入 t r r o o t v tr_{root_v} trrootv 来代替
同样的第
k
k
k 个出队的就是
s
s
s 到
t
t
t 的
k
k
k 短路。
具体看代码
#include <bits/stdc++.h>
using namespace std;
#define pii pair<double,int>
const double ep = 1e-8;
const int N = 10005;
const int M = 200005;
int n,m;
double E;
struct node{
int id,v;
double w;
};
vector<node>road[N],rroad[N];
double dist[N];
int root[N];
namespace LT{
struct point{
int lson,rson,last;
int dist;
double val;
}tr[M * 20];
int cnt = 0;
int create(double v,int last)
{
int u = ++cnt;
tr[u].val = v;
tr[u].last = last;
tr[u].dist = tr[u].lson = tr[u].rson = 0;
return cnt;
}
int cop(int id)
{
int u = ++cnt;
tr[u] = tr[id];
return u;
}
int merge(int x,int y)
{
if(x == 0 || y == 0)
{
return x | y;
}
if(tr[x].val > tr[y].val)
{
swap(x,y);
}
int u = cop(x);
tr[u].rson = merge(tr[u].rson,y);
if(tr[tr[u].lson].dist < tr[tr[u].rson].dist)
{
swap(tr[u].lson,tr[u].rson);
}
tr[u].dist = tr[tr[u].rson].dist + 1;
return u;
}
void insert(int &rt,double val,int last)
{
rt = merge(create(val,last),rt);
}
}
using namespace LT;
priority_queue<pii,vector<pii>,greater<pii> >q;
void dij()
{
for(int i = 0;i < N;i++)
{
dist[i] = 1e9;
}
dist[n] = 0;
q.push((pii){0.0,n});
while(!q.empty())
{
int u = q.top().second;
double len = q.top().first;
q.pop();
if(dist[u] != len)
{
continue;
}
for(int i = 0;i < rroad[u].size();i++)
{
int v = rroad[u][i].v;
double l = rroad[u][i].w;
if(dist[v] > dist[u] + l)
{
dist[v] = dist[u]+ l;
q.push((pii){dist[v],v});
}
}
}
}
int f[N];
int st[N];
bool vis[N];
int top;
bool line[M];
void dfs(int u)
{
st[++top] = u;
vis[u] = true;
for(int i = 0;i < rroad[u].size();i++)
{
int v = rroad[u][i].v;
double l = rroad[u][i].w;
if(dist[v] == dist[u] + l && vis[v] == false)
{
line[rroad[u][i].id] = true;
f[v] = u;
dfs(v);
}
}
}
void build()
{
for(int i = 1;i<=top;i++)
{
int u = st[i];
root[u] = root[f[u]];
for(int i = 0;i < road[u].size();i++)
{
int id = road[u][i].id;
int v = road[u][i].v;
double w = road[u][i].w;
if(!line[id] && dist[v] != dist[0])
{
insert(root[u],dist[v] + w - dist[u],v);
}
}
}
}
int kshort()
{
int cnt = 1;
E-=dist[1];
priority_queue<pii,vector<pii>,greater<pii> >q;
q.push((pii){dist[1] + tr[root[1]].val,root[1]});
while(!q.empty())
{
double len = q.top().first;
int u = q.top().second;
q.pop();
if(len -ep>E)break;
E-=len - ep;
cnt++;
int l = tr[u].lson;
int r = tr[u].rson;
int top = tr[u].last;
if(root[top])
{
q.push((pii){len + tr[root[top]].val,root[top]});
}
if(l)
{
q.push((pii){len + tr[l].val - tr[u].val,l});
}
if(r)
{
q.push((pii){len + tr[r].val - tr[u].val,r});
}
}
return cnt;
}
int main()
{
scanf("%d%d%lf",&n,&m,&E);
for(int i = 1;i <= m;i++)
{
int u,v;
double w;
scanf("%d%d%lf",&u,&v,&w);
if(u == n)
{
i--;
m--;
continue;
}
road[u].push_back((node){i,v,w});
rroad[v].push_back((node){i,u,w});
}
dij();
dfs(n);
build();
printf("%d\n",kshort());
return 0;
}