[POJ 2449] Remmarguts' Date (A*搜索)

24 篇文章 0 订阅
2 篇文章 0 订阅

题目传送门

题意描述:

给你N个点和M条单向边 给出起点和终点 求出从起点到终点的第K短路

朴素做法:

我们如果用BFS进行搜索的话,记录一下终点出队的次数,当出队K次时,当前的路程即为第K短路
但点数过大时,入队列的节点过多,时间和空间复杂度都较高。

A*优化

A*是在搜索中常用的优化,一种启发式搜索。
简单讲就是对点进行估计,然后跑优先跑最有可能得出最终答案的点。
它可以用公式表示为f(n) = g(n) + h(n),其中,f(n)是从s经由节点n到t的估价函数,g(n)是在状态空间中从s到n的实际代价,h(n)是从n到t的最佳路径估计代价。在设计中,要保证h(n)<= n到t的实际代价,这一点很重要,h(n)越接近真实值,速度越快。
由于启发函数的作用,使得计算机在进行状态转移时尽量避开不可能产生最优解的分支,而选择相对较接近最优解的路径进行搜索,降低了时间和空间复杂度。

具体实现

对于h(n)的处理,我们可以直接建反图,然后跑SPFA,这样就可以得到每个点的h(n)值.
1.先在反图上跑SPFA
2.初始化第一个点 f(s)=0+dis[s]
3.对邻接点进行状态转移,扔入优先队列
4.记录每个点出队次数,找到答案

注意点:

1.队列必须用优先队列,因为我们需要前K小的值才恰好是K小路
2.当起点和终点重合时,K++

AC Code

#include <queue>
#include <iostream>
#include <cstdio>
#include <cstring>
#define il inline
#define cnt Cnt[f]
using namespace std;
const int maxm=1e7+1;
int inf;
int head[2][maxm],to[2][maxm*2],net[2][maxm*2],cost[2][maxm*2],Cnt[2];
int n,m,k;
il void add_edge(int s,int t,int c,int f)
{
    cnt++;
    to[f][cnt]=t,cost[f][cnt]=c;
    net[f][cnt]=head[f][s],head[f][s]=cnt;
}
int dis[maxm],vis[maxm];
il void SPFA(int s,int t)
{   
    queue <int> dl;
    memset(dis,127/3,sizeof(dis));
    inf=dis[0];
    dl.push(s),vis[s]=1,dis[s]=0;
    while(!dl.empty())
    {
        int x=dl.front();
        dl.pop();
        vis[x]=0;
        for(int i=head[0][x];i;i=net[0][i])
        {
            int tmp=to[0][i],c=cost[0][i];
            if(dis[tmp]>dis[x]+c)
            {
                dis[tmp]=dis[x]+c;
                if(!vis[tmp]) dl.push(tmp),vis[tmp]=1;
            }
        }
    }
}
struct node  
{  
    int f,g;//f=g+dis[v] 
    int v;//当前的点 
    node(int a,int b,int c):f(a),g(b),v(c){}
    bool operator < (const node& a) const  
    {  
        return a.f < f;  
    }  
};  
int cur[maxm];//记录每个点出队次数 
il int A_star(int s,int t)
{
    priority_queue<node> dl; 
    if(dis[s]==inf) return -1;
    dl.push((node){dis[s],0,s});
    while(!dl.empty())
    {
        node now=dl.top();
        dl.pop(),cur[now.v]++;
        if(cur[t]==k) return now.f;
        if(cur[now.v]>k) continue;
        for(int i=head[1][now.v];i;i=net[1][i])
        {
            int tmp=to[1][i],c=cost[1][i];
            dl.push((node){now.g+c+dis[tmp],now.g+c,tmp});
        }
    } 
    return -1;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v,c;
        scanf("%d%d%d",&u,&v,&c);
        add_edge(u,v,c,1),add_edge(v,u,c,0);
    }
    int start,end;
    scanf("%d%d%d",&start,&end,&k);
    if(start==end) k++;
    SPFA(end,start);
    printf("%d\n",A_star(start,end));
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值