poj Remmarguts' Date A*+ spfa

题目链接:http://poj.org/problem?id=2449
题意:求s到t的第k短路。
以前听说过启发式搜索A*算法,现在是第一次自己写。还顺便了解了spfa与dijstra的区别。
dijstra算法用于边权为正的图中。每次松弛操作都是由已经确定了最短路的点到一个不确定的点。比如从起点开始,起初集合中只有源点s,起点s到起点的最短路0是已经确定了的。然后向与其直接相连且最短路不确定的点扩展,将点加入集合,扩展后又可以得到确定了最短路的点。从集合中选出最小的点去松弛其他点,这个最小的点不可能再通过其他点松弛变得更小,因为是最小的点,其他在集合中的点要么比它大,要么等于它。在加上一个正边到这个最小的边一定是大于最小的边。dijstra不会再次松弛这样的一个点。(在代码中就是vis标记过的点不会再处理)。但是如果边中有负边,则有可能会使得其他较大的边通过一个负边,变得比最小的还小,那之前通过最小的边松弛的边,则还能通过已经变小的边再次松弛。所以需要将最小的边再次加入到“集合”中。

也就是一个点改进过其它的点之后,过了一段时间可能本身被改进,于是再次用来改进其它的点,这样反复迭代下去。设一个点用来作为迭代点对其它点进行改进的平均次数为k,有办法证明对于通常的情况,k在2左右。

#include<cstdio>    // A* 算法求第k短路 
#include<queue>     // 估价函数: f(i) = h(i) + g(i)   ,  g(i) 表示从s到i的实际距离 
#include<iostream>  //                                    h(i)  表示i到终点t的最短距离  
#include<vector>    //  做类似BFS的扩展,优先选取f小的点扩展 
#include<map>       //  BFS由于是在边权为1的图上找最短路,所以队列的头部点一定是距离起点最近的点 
#include<cstring>   //  距离起点越近会先入队列,所以当第一次到达终点时的距离即为终点到起点的最短距离 
#include<string>    //  当第k次到达终点时,即使第k短路. 
#include<set>
#include<stack>
#include<algorithm>
#define cle(a) memset(a,0,sizeof(a))
#define inf(a) memset(a,0x3f,sizeof(a))
#define ll long long
#define Rep(i,a,n) for(int i=a;i<=n;i++)
using namespace std;
const int INF = ( 2e9 ) + 2;
const ll maxn = 1010;
int n,m,s,t,k;
int dist[maxn],vis[maxn];
struct node
{
    int v,d;
    bool operator < (const node &b)const
    {
        return this->d>b.d;
    }
};
struct D
{
    int u,g,f;
    bool operator < (const D &b)const
    {
        return f>b.f;
    }
};
struct edge
{
    int to,w;
};
vector<edge> rg[maxn],g[maxn];
void spfa(int s)
{
    inf(dist);
    dist[s]=0;
    queue<int> q;
    q.push(s);
    vis[s]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=0;i<rg[u].size();i++)
        {
            int v=rg[u][i].to;
            int w=rg[u][i].w;
            if(dist[v]>dist[u]+w)
            {
                dist[v]=dist[u]+w;
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
//void dijstra(int s)
//{
//  inf(dist);
//  dist[s]=0;
//  priority_queue<node> q;
//  q.push(node{s,dist[s]});
//  memset(vis,0,sizeof(vis));
//  while(!q.empty())
//  {
//      node a = q.top();q.pop();
//      int u = a.v;
//      
//      if(vis[u])continue;
//      vis[u]=1;
//      for(int i=0;i<rg[u].size();i++)
//      {
//          int v=rg[u][i].to;
//          if(vis[v])continue;
//          if(dist[v]>dist[u]+rg[u][i].w)
//          {
//              dist[v]=dist[u]+rg[u][i].w;
//              q.push(node{v,dist[v]});
//          }
//      }
//  }
//}
int Astar(int s,int t,int k)
{
    if(s==t)k++;
    if(dist[s]==INF)return -1;
    int c=0;
    priority_queue<D> q;
    q.push(D{s,0,dist[s]});
    while(!q.empty())
    {
        D a=q.top();q.pop();
        int u=a.u;
        if(u==t)c++;
        if(c==k)return a.g;
        for(int i=0;i<g[u].size();i++)
        {
            int v=g[u][i].to;
            if(dist[v]==INF)continue;
            int G=a.g+g[u][i].w;
            int f=G+dist[v];
            q.push(D{g[u][i].to,G,f});
        }
    }
    return -1;
}
int main()
{
//  freopen("out1.txt","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        g[u].push_back(edge{v,w});
        rg[v].push_back(edge{u,w});
    }
    scanf("%d%d%d",&s,&t,&k);
//  dijstra(t);
    spfa(t);
    int ans=Astar(s,t,k);
    printf("%d\n",ans);

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值