poj2449 Remmarguts' Date

poj2449 Remmarguts’ Date

这道题。。。。。其实我从最开始就做对了,但总是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");
    }
}

至于这个算法的正确性。。。。我目前还是不理解的,也许以后会理解吧,到时候再回来改博客。。。

你可能感兴趣的:(a)