[poj2449]Remmarguts' Date(k短路学习笔记)(未完成)

题目大意就是给出一个图,然后一个起点一个终点,求这两点间的第K短路。


一般做法:SPFA+A*
k短路的话,我们可以马上想到一个宽搜的做法,从st开始宽搜,当第k次搜索到ed时,所得到的长度即所求。
但是这种方法产生特别多的冗杂状态,因为我们每次的选择是无目标的,那么这时候我们就想:怎么能使得我们的选择有目标呢?
那么就要用到A*惹。
A*的精髓就在估价函数 f ( x ) ,具体是什么样子的呢。
我们设图中的一个点x,从起点到x已经走过的距离为 g ( x ) ,到达终点要走的最短距离 h ( x ) 。那么有

f ( x ) = g ( x ) + h ( x )

其中 f ( x ) 为x的估价函数,当我们在x点的时候,我们会根据估价函数的大小来选择下一条延展的边,这样带有指向性的选择可以加强算法的效率。
新的问题是,当我们到点x的时候,我们只知道 g ( x ) ,但 h ( x ) 是不知道的。
那么我们只要建一个反图,从ed向st跑一边最短路,得到的dis就是 h ( x ) 啦。
那么A*部分的实现思路就是:
用一个堆来维护所有状态的估价函数值,每次从堆中取出估价函数最小的状态,枚举下一步走的边,将新的状态及其估价值放入堆中,直到找到k条st到ed的路径为止。
注意当st=ed的时候要算(k+1)短路,因为st到ed这条边权为0的边是不能算进k短路里面的。
好啦,A*就这样讲完啦,大家结合代码理解一下吧!
觉得同学的模版写的特好看,就不特地去写了(懒

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct node
{
    int x,y,d,next;
}a[510000],b[510000];
int len1,last1[11000],len2,last2[11000]; 
struct As
{
    int f,g,to;//f==d[x]+g,用来维护优先队列 g表示出发走了多少距离,to相当于边目录的y 
    bool operator<(const As a)const
    {
        if(a.f==f)return a.g<g;
        return a.f<f;
    }
};
priority_queue<As>Q;
void ins1(int x,int y,int d)
{
    len1++;
    a[len1].x=x;a[len1].y=y;a[len1].d=d;
    a[len1].next=last1[x];last1[x]=len1;
}
void ins2(int x,int y,int d)
{
    len2++;
    b[len2].x=x;b[len2].y=y;b[len2].d=d;
    b[len2].next=last2[x];last2[x]=len2;
}
int st,ed,k,d[11000],list[11000];
bool v[11000];
void SPFA()
{
    memset(d,63,sizeof(d));d[ed]=0;
    memset(v,false,sizeof(v));v[ed]=true;
    int head=1,tail=2;list[1]=ed;
    while(head!=tail)
    {
        int x=list[head];
        for(int k=last2[x];k;k=b[k].next)
        {
            int y=b[k].y;
            if(d[y]>d[x]+b[k].d)
            {
                d[y]=d[x]+b[k].d;
                if(v[y]==false)
                {
                    v[y]=true;
                    list[tail]=y;
                    tail++;if(tail==10000)tail=1;
                }
            }
        }
        v[x]=false;
        head++;if(head==10000)head=1;
    }
}
void A_star()  
{  
    int cnt=0;As x,y;
    x.to=st,x.g=0,x.f=x.g+d[st];
    Q.push(x);
    while(Q.empty()==false)
    {
        x=Q.top();Q.pop();
        if(x.to==ed)//由于优先队列,先找到的肯定是最好的,由此类推 
        {
            cnt++;
            if(cnt==k){printf("%d\n",x.g);return ;}
        }
        for(int k=last1[x.to];k;k=a[k].next)
        {
            y.to=a[k].y;
            y.g=x.g+a[k].d;
            y.f=y.g+d[y.to];
            Q.push(y);
        }

    }
    printf("-1\n");
}  
int main()
{
    int n,m,X,Y,D;
    scanf("%d%d",&n,&m);
    len1=0;memset(last1,0,sizeof(last1));
    len2=0;memset(last2,0,sizeof(last2));
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&X,&Y,&D);
        ins1(X,Y,D);ins2(Y,X,D);
    }
    scanf("%d%d%d",&st,&ed,&k);
    SPFA();
    if(st==ed)k++;
    if(d[st]>999999999){printf("-1\n");return 0;}
    A_star();
    return 0;
}

但是!!
即使用A*优化过,这样的算法还是非常的慢的!
时间复杂度是O(nk)!!


高级做法:可持久化堆(可并堆)
首先推荐大家看论文:《俞鼎力-堆的可持久化与k短路》
然后我还没学完呢,先发着这篇博客先

猜你喜欢

转载自blog.csdn.net/cabi_zgx/article/details/78936725