这道题。。。。。其实我从最开始就做对了,但总是Wrong Answer。。。。。后来才发现原来是忽略了一个条件,英语硬伤啊。
给你一张图,第一行是N和M,及点数和边数
后面M行依次是三个整数A、B、T,表示从点A到点B有一条长度为T的边,最后一行S、T、K,表示询问从S到T第K短的路
值得注意的是,必须是从S点走出到T点进入的距离,就是说如果S=T,那么第一短的距离并不是0(我说的够清楚了)。
要求出第k短路,办法是 单源最短路 + A*
首先,将所有的边反向,从T点出发做一次单源最短路,记录数组dist[i]表示从i结点到T点的最短距离。将dist[i]作为启发式函数,那么f[i]=g[i]+dist[i],其中g[i]是从S点出发到i点已经经过的实际距离,那么f[i]就代表当前结点的估计代价。
维护一个堆,从从S点开始搜索,f[S]=dist[S],g[S]=0,将S点放入堆中。每次取出堆顶元素x(f值最小),用它的值来更新与它相邻的边,即对于任意 j∈x的儿子,让g[j]←g[x]+w(x,j),f[j]←g[j]+dist[j],再将j放进堆。。。。。重复操作
直到发现T出队(比入队更好写),则cnt++,当cnt=K时,发现答案(即g[T]),退出搜索
已经搜出到达点T的第K短路时,每一个节点最多入队了K次,即在最坏情况下,时间复杂度是O(Kn+ke),后一个k是SPFA算法的k
//poj2449 第k短路 a*
/* 令f[x]=g[x]+h[x] 其中g[x]是实际代价(即已经过边的权值),h[x]是启发式函数 */
#include<cstdio>
#include<queue>
#include<cstring>
#define maxn 1010
#define maxm 201000
using namespace std;
int N, M, K, S, T, head[maxn], to[maxm], next[maxm], w[maxm], tot, dist[maxn],
ans, f[maxn], vis[maxn], g[maxn];
struct node
{
int n, f;
bool operator<(const node x)const{return f>x.f;}
};
queue<int> q;
priority_queue<node> heap;
void add(int a, int b, int c)
{
to[++tot]=b;
w[tot]=c;
next[tot]=head[a];
head[a]=tot;
}
void input()
{
int i,j,a,b,c;
tot=0;
memset(head,0,sizeof(head));
memset(next,0,sizeof(next));
for(i=1;i<=M;i++)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);add(b,a,c);
}
scanf("%d%d%d",&S,&T,&K);
if(S==T)K++;
while(q.size())q.pop();
while(heap.size())heap.pop();
ans=0;
}
void a()
{
int p, cnt=0;
node x;
f[S]=dist[S];
g[S]=0;
heap.push( (node){S,f[S]} );
while(!heap.empty())
{
x=heap.top();heap.pop();
if(x.n==T && ++cnt==K){ans=x.f;break;}
for(p=head[x.n];p;p=next[p])
{
if(~p&1)continue;
g[ to[p] ]=x.f-dist[x.n]+w[p];
f[ to[p] ]=g[ to[p] ]+dist[ to[p] ];
heap.push( (node){ to[p],f[ to[p] ]} );
}
}
}
void spfa()
{
int p, x;
memset(f,0,sizeof(f));
memset(vis,0,sizeof(vis));
memset(dist,0x3f,sizeof(dist));
q.push(T);dist[T]=0;
vis[T]=true;
while(!q.empty())
{
x=q.front();q.pop();
vis[x]=false;
f[x]++;
for(p=head[x];p;p=next[p])
{
if(p&1)continue;
if( dist[to[p]] > dist[x]+w[p])
{
dist[to[p]]=dist[x]+w[p];
if(!vis[to[p]])
{q.push(to[p]);vis[to[p]]=true;}
}
}
}
}
int main()
{
while(scanf("%d%d",&N,&M)!=EOF)
{
input();
spfa();
memset(f,0,sizeof(f));
memset(f,0x3f,sizeof(f));
a();
if(ans)printf("%d\n",ans);
else printf("-1\n");
}
}
至于这个算法的正确性。。。。我目前还是不理解的,也许以后会理解吧,到时候再回来改博客。。。