所谓K短路,就是从s到t的第K短的路,第1短就是最短路。
如何求第K短呢?有一种简单的方法是广度优先搜索,记录t出队列的次数,当t第k次出队列时,就是第k短路了。但点数过大时,入队列的节点过多,时间和空间复杂度都较高。
A*是在搜索中常用的优化,一种启发式搜索。简单的说,它可以用公式表示为f(n) = g(n) + f(n),其中,f(n)是从s经由节点n到t的估价函数,g(n)是在状态空间中从s到n的实际代价,h(n)是从n到t的最佳路径估计代价。在设计中,要保证h(n)<= n到t的实际代价,这一点很重要,h(n)越接近真实值,速度越快。
由于启发函数的作用,使得计算机在进行状态转移时尽量避开不可能产生最优解的分支,而选择相对较接近最优解的路径进行搜索,降低了时间和空间复杂度。
算法过程:
1. 将图反向,用dijstra+heap求出t到所有点的最短距离,目的是求所有点到点t的最短路,用dis[i]表示i到t的最短路,其实这就是A*的启发函数,显然:h(n)<= n到t的实际代价。
2. 定义估价函数。我们定义g(n)为从s到n所花费的代价,h(n)为dis[n],显然这符合A*算法的要求。
3. 初始化状态。状态中存放当前到达的点i,fi,gi。显然,fi=gi+dis[i]。初始状态为(S,dis[S],0),存入优先级队列中。
4. 状态转移。假设当前状态所在的点v相邻的点u,我们可以得到转换:(V,fv,gv)-->(U,fu+w[v][u],gv+w[v][u])。
5. 终止条件。每个节点最多入队列K次,当t出队列K次时,即找到解。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define mpair make_pair
#define pii pair<int,int>
#define MM(a,b) memset(a,b,sizeof(a));
typedef long long lld;
typedef unsigned long long u64;
template<class T> bool up_max(T& a,const T& b){return b>a? a=b,1 : 0;}
template<class T> bool up_min(T& a,const T& b){return b<a? a=b,1 : 0;}
#define maxn 1005
const int inf= 2100000000;
int n,m;
vector< pii > e[maxn], e2[maxn];
int dis[maxn];
bool vis[maxn];
void Dijkstra(int st)
{
for(int i=1;i<=n;++i) dis[i]= inf, vis[i]= 0;
dis[st]= 0, vis[st]= 1;
for(int i=0;i<e2[st].size();++i)
up_min( dis[ e2[st][i].first ], e2[st][i].second );
while(1){
int mdis= inf, idx= -1;
for(int i=1;i<=n;++i){
if( !vis[i] && up_min( mdis, dis[i] ) )
idx= i;
}
if( -1 == idx ) break;
vis[idx]= 1;
for(int i=0;i<e2[idx].size();++i){
int v= e2[idx][i].first, w= e2[idx][i].second;
if( !vis[v] )
up_min( dis[v], dis[idx] + w );
}
}
}
struct Node
{
int u, h, g; /// h[u]+g[u];
Node(int u_,int h_,int g_)
{u=u_,h=h_,g=g_;}
bool operator<(Node a)const
{
return h+g > a.h+a.g;
}
};
priority_queue<Node>Q;
int cnt[maxn];
int Astar(int st, int ed, int k){ /// A*: return the k-th shortest path from st to ed;
while( !Q.empty() ) Q.pop();
fill( cnt, cnt+1+n, 0 );
Q.push( Node( st, 0, dis[st] ) );
while( !Q.empty() ){
Node a= Q.top(); Q.pop();
int u= a.u, h= a.h, g= a.g;
++cnt[u];
if( cnt[u]==k && u==ed ) return h;
if( cnt[u]>k ) continue;
for(int i=0;i<e[u].size();++i){
int v= e[u][i].first, w= e[u][i].second;
Q.push( Node( v, h+w, dis[v] ) );
}
}
return -1;
}
int main()
{
while( cin>>n>>m )
{
for(int i=1;i<=n;++i)
e[i].clear(), e2[i].clear();
while(m--)
{
int u,v,w;
scanf("%d%d%d", &u, &v, &w);
e[u].push_back( pii( v, w ) );
e2[v].push_back( pii( u, w ) );
}
int st, ed, k;
cin>>st>>ed>>k;
if( st==ed ) ++k;
Dijkstra( ed ); /// ed;
cout<< Astar( st, ed, k ) <<endl;
}
}