A*算法+最短路实现K短路+模板题

<font color=black size=4>K短路问题还是很普遍的,了解一下K短路很有必要,顺便学会A*的简单应用更好。
A*算法,是一种启发式搜索算法,我们可以自己设定一个估价函数,这样我们的搜索过程就会优先选择有更好的估价函数的点进行搜索。
在最短路中,A*的估价函数定义如下:f(p)=g(p)+h(p),g(p)表示当前从起始点s到点p的距离,而h(p)就是从终点到p的最短路距离,(暂且是最短路,因为k短路必须由最短路出发向前找的,每次都是除了上一次的最短路),则f(p)就意味着从起点s到当前路径p之后再走到终点t的最短路,这样,我们的每次扩展都是有方向的扩展,从而降低求解速度和降低搜索的状态数,并且保证不优于最优解的同时尽量靠近最优解。
思路到了,做法也必须跟上。我们用最短路来估价,这就要求对h(p)函数进行一个预处理,所以我们可以求出终点到各个点的最短路,作为h(p)值,再从s开始遍历k次,每次都以上一次为最短路,直到我们第k次到达终点,此时的路径长度就是k短路。
</font>

<font color=black size=4>
详细部分看下面几个模板题的代码。
(1)POJ2449   
题目链接:https://vjudge.net/problem/POJ-2449
题目大意:给出一个图,然后给出一个起点个一个终点,求这两点间的第K短路。
本题中是可以走重复的路的,所以如果一张图中有一个环的话,无论求第几短路都是存在的。
也就是,样例询问的第二短路,就是先从1到2,再从2到1,再从1到2,结果为5+4+5=14
 
 代码:</font>
 

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;

/*
    POJ 2449  A*求k短路模板题
*/

const int maxn=1005;
const int maxm=500005;
const int INF=0x3f3f3f3f;

struct Edge
{
    int u;
    int v;
    int next;
    int w;
}edges[maxm],re_edges[maxm];//re_edges为反向边

struct ANode
{
    int f,g,v;//分别对应f函数,g函数和v编号
    bool operator <(const ANode &rhs)const
    {
        if(rhs.f==f)return rhs.g<g;
        return f>rhs.f;//f小的优先被考虑
    }
    ANode(int _f,int _g,int _v):f(_f),g(_g),v(_v){}
};
int head[maxn],re_head[maxn],tot;
void init()
{
    memset(head,-1,sizeof(head));
    memset(re_head,-1,sizeof(re_head));
    tot=0;
}
int dis[maxn];
int vis[maxn];
int n,m,S,T,k;

void add_edges(int u,int v,int cost)
{
    edges[tot].u=u;
    edges[tot].v=v;
    edges[tot].w=cost;
    edges[tot].next=head[u];
    head[u]=tot;
    //反向边,因为要计算点p到最终点的最短路,就直接计算最终点到p的最短路
    re_edges[tot].u=v;
    re_edges[tot].v=u;
    re_edges[tot].w=cost;
    re_edges[tot].next=re_head[u];
    re_head[u]=tot++;
}

//处理一个距离数组,表示从终点开始到各个点的最短路

int cnt[maxn];//每个点的入队次数
bool SPFA(int s)
{
    for(int i=1;i<=n;i++)dis[i]=INF;
    memset(vis,0,sizeof(vis));
    queue<int>Q;
    while(!Q.empty())Q.pop();
    vis[s]=1;
    dis[s]=0;
    Q.push(s);
    memset(cnt,0,sizeof(cnt));
    cnt[s]=1;
    while(!Q.empty())
    {
        int u=Q.front();Q.pop();
        vis[u]=0;
        for(int i=re_head[u];i!=-1;i=re_edges[i].next)
        {
            int v=edges[i].v;
            int w=edges[i].w;
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                if(!vis[v])
                {
                    Q.push(v);
                    vis[v]=1;
                    if(++cnt[v]>n)return false;
                }
            }
        }
    }
    return true;
}

int A_star(int s,int t)
{
    int cnt=0;//计数,计k短路
    priority_queue<ANode>Q;
    if(s==t)k++;
    if(dis[s]==INF)return -1;
    Q.push(ANode(dis[s],0,s));
    while(!Q.empty())
    {
        ANode temp=Q.top();Q.pop();
        int u=temp.v;
        if(u==t)
        {
            cnt++;
            if(cnt==k)return temp.g;
        }
        for(int i=head[u];i!=-1;i=edges[i].next)
        {
            //移动,更新节点的估计函数
            int v=edges[i].v;
            int g=temp.g+edges[i].w;
            int f=g+dis[v];
            Q.push(ANode(f,g,v));
        }
    }
    return -1;
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        for(int i=0;i<m;i++)
        {
            int x,y,w;
            scanf("%d%d%d",&x,&y,&w);
            add_edges(x,y,w);
        }
        scanf("%d%d%d",&S,&T,&k);
        if(SPFA(S))
        {
            printf("%d\n",A_star(S,T));
        }
        else
        {
            printf("-1\n");
        }
    }
    return 0;
}

<font color=black size=4>(2)2018 ACM-ICPC沈阳网络赛——D——Made in Heaven

题目链接:https://nanti.jisuanke.com/t/31445

也是一个k短路裸题,模板改一改就能过啦。

代码:</font>

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;

/*
      A*求k短路模板题
*/

const int maxn=1005;
const int maxm=10000+50;
const int INF=0x3f3f3f3f;

struct Edge
{
    int u;
    int v;
    int next;
    int w;
}edges[maxm],re_edges[maxm];//re_edges为反向边

struct ANode
{
    int f,g,v;//分别对应f函数,g函数和v编号
    bool operator <(const ANode &rhs)const
    {
        if(rhs.f==f)return rhs.g<g;
        return f>rhs.f;//f小的优先被考虑
    }
    ANode(int _f,int _g,int _v):f(_f),g(_g),v(_v){}
};
int head[maxn],re_head[maxn],tot;
void init()
{
    memset(head,-1,sizeof(head));
    memset(re_head,-1,sizeof(re_head));
    tot=0;
}
int dis[maxn];
int vis[maxn];
int n,m,S,T,k;

void add_edges(int u,int v,int cost)
{
    edges[tot].u=u;
    edges[tot].v=v;
    edges[tot].w=cost;
    edges[tot].next=head[u];
    head[u]=tot;
    //反向边,因为要计算点p到最终点的最短路,就直接计算最终点到p的最短路
    re_edges[tot].u=v;
    re_edges[tot].v=u;
    re_edges[tot].w=cost;
    re_edges[tot].next=re_head[u];
    re_head[u]=tot++;
}

//处理一个距离数组,表示从终点开始到各个点的最短路

int cnt[maxn];//每个点的入队次数
bool SPFA(int s)
{
    for(int i=1;i<=n;i++)dis[i]=INF;
    memset(vis,0,sizeof(vis));
    queue<int>Q;
    while(!Q.empty())Q.pop();
    vis[s]=1;
    dis[s]=0;
    Q.push(s);
    memset(cnt,0,sizeof(cnt));
    cnt[s]=1;
    while(!Q.empty())
    {
        int u=Q.front();Q.pop();
        vis[u]=0;
        for(int i=re_head[u];i!=-1;i=re_edges[i].next)
        {
            int v=edges[i].v;
            int w=edges[i].w;
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                if(!vis[v])
                {
                    Q.push(v);
                    vis[v]=1;
                    if(++cnt[v]>n)return false;
                }
            }
        }
    }
    return true;
}

int A_star(int s,int t)
{
    int cnt=0;//计数,计k短路
    priority_queue<ANode>Q;
    if(s==t)k++;
    if(dis[s]==INF)return -1;
    Q.push(ANode(dis[s],0,s));
    while(!Q.empty())
    {
        ANode temp=Q.top();Q.pop();
        int u=temp.v;
        if(u==t)
        {
            cnt++;
            if(cnt==k)return temp.g;
        }
        for(int i=head[u];i!=-1;i=edges[i].next)
        {
            //移动,更新节点的估计函数
            int v=edges[i].v;
            int g=temp.g+edges[i].w;
            int f=g+dis[v];
            Q.push(ANode(f,g,v));
        }
    }
    return -1;
}

int main()
{
    int time;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        scanf("%d%d%d%d",&S,&T,&k,&time);
        for(int i=0;i<m;i++)
        {
            int x,y,w;
            scanf("%d%d%d",&x,&y,&w);
            add_edges(x,y,w);
        }
        if(SPFA(T))
        {
            int ans=A_star(S,T);
            if(ans==-1||ans>time)
                printf("Whitesnake!\n");
            else
                printf("yareyaredawa\n");
        }
        else
            printf("yareyaredawa\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Q755100802/article/details/82814395