zoukankan      html  css  js  c++  java
  • 图论1-k短路

    相信大家都已经会求最短路了,k短路是在最短路熟练掌握的情况下进行的一个进阶,要求把最短路的过程想的十分通透才能学的懂。

    先摆出一道例题做引例:

    http://poj.org/problem?id=2449 POJ2449 Remmarguts' Date

    大概题意就是求从s到t的第k短路,注意要反着建边。同时如果s==t,什么都不走的这条路不算,也就是if(s==t) k++;

    如果大家题都读懂了就往下看。

    这是一个十分令人困惑的题,之前学的最短路算法都只能记录一个dis,也就是从s到这个点的最短距离,可我们这次要求的k短路,

    我们该怎么办呢?因为SPFA是从bfs扩展出来的,所以我们先从bfs想,如果是bfs的话,那第k短路就是第k次访问t时的路径,但是

    SPFA中没法这样做啊,所以要新开一个函数来做这件事。但是能bfs的条件是所有边得权值都一样,但不一样怎么办呢?我们必须

    要先扩展代价较小的,但如何确定哪个点的代价小呢?我们必须要一个估价函数,也就是A*(A star),这个估价函数必须要满足

    比真实代价小。但是这个估价该怎么算呢,我们会发现可以由现有代价加上这个点到t点的代价就可以是它的估价,满足成为估价的

    条件,大概的思路已经理清楚了。现在,我在串一遍整个思路。

    1.读入,建图。注意要正反图都要建,因为我们算任意一点到s要建反图,但是A*时要正图,所以我们正反图都要建。

    2.反图跑一遍spfa,初始化出所有点到s的代价,以便估价函数运算。

    3.正图A*,并找出答案,每个结构体有三个量,分别是x,ans,f,表示当前节点编号,答案,以及估价,priority_queue按估价升序排序即可。

    注意:

    1.当s==t是k要++.

    2.记得输出-1

    3.记得要建两个图

    下面就是代码了:

    #include<iostream>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int NR=1e5+10;
    const int INF=0x3f3f3f3f;//极大值 
    int n,m;
    int dis[NR];//算估价时要用,用每个点到t的最小值 
    int tot;//边的总数 
    int head1[NR],head2[NR];//正反两个图 
    bool flag[NR];//spfa的时候判断是否在队列里 
    struct edge
    {
        int to;
        int w;
        int next;
    }e[NR<<1],e2[NR<<1];//邻接表正常操作 
    struct Nd
    {
        int f,ans,x;
        bool operator < (Nd A)const//按估价升序排列,如果估价相等就看真实的代价 
        {
            if(A.f==f) return ans>A.ans;
            return f>A.f;
        }
    };
    void add(int u,int v,int w)
    {
        tot++;
        e[tot].to=v;
        e[tot].w=w;
        e[tot].next=head1[u];
        head1[u]=tot;
        e2[tot].to=u;
        e2[tot].w=w;
        e2[tot].next=head2[v];
        head2[v]=tot;
        //两遍建图 
    }
    void spfa(int t)//默认spfa会打 
    {
        for(int i=1;i<=n;i++) dis[i]=INF;
        dis[t]=0;
        queue<int> q;
        q.push(t);
        flag[t]=1;
        while(!q.empty())
        {
            int x=q.front();
            q.pop();flag[x]=0;
            for(int i=head2[x];i;i=e2[i].next)
            {
                int y=e2[i].to,w=e2[i].w;
                if(dis[y]>dis[x]+w)
                {
                    dis[y]=dis[x]+w;
                    if(!flag[y])
                    {
                        q.push(y);
                        flag[y]=1;
                    }
                }
            }
            
        }
    }
    int a_star(int s,int t,int k)//进入A*函数 
    {
        if(s==t) k++;//如果起终点相同,不能站着不动,就是要去掉一种所以k++ 
        if(dis[s]==INF) return -1;//如果都到不了,就没有扩展的必要了 
        priority_queue<Nd> q;
        int cnt=0;Nd x,to;
        x.x=s;x.ans=0;//初值 
        x.f=x.ans+dis[x.x];//计算出使估价 
        q.push(x);
        while(!q.empty())
        {
            x=q.top();q.pop();
            if(x.x==t) cnt++;//每一次经过终点,就找到了一条路,所以cnt要++ 
            if(cnt==k) return x.ans;//如果找到第k短路了,结束A*函数 
            for(int i=head1[x.x];i;i=e[i].next)
            {
                to.x=e[i].to;
                to.ans=x.ans+e[i].w;
                to.f=to.ans+dis[to.x];
                q.push(to);
                //每次扩展 
            }
        }
        return -1;//没有第k短路输出-1 
    }
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
        while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
        return x*f;
    }
    int main()
    {
        n=read(),m=read();
        for(int i=1;i<=m;i++)
        {
            int x=read(),y=read(),z=read();
            add(x,y,z);//建图 
        }
        int s=read(),t=read(),k=read();    
        spfa(t);//从t开始跑最短路 
        int ans=a_star(s,t,k);//得到答案 
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    valueOf与toString
    责任链模式
    Js中Symbol对象
    Vue路由懒加载
    updatedb命令
    策略模式
    Docker(3)- Centos 7.x 下 Docker 镜像加速配置
    Vmware
    Docker(2)- Centos 7.x 下安装 Docker
    Docker(1)- 什么是 Docker
  • 原文地址:https://www.cnblogs.com/chen-1/p/12552822.html
Copyright © 2011-2022 走看看