《算法竞赛进阶指南》0x27 A*算法求解第K短路 POJ2449

题目链接:http://poj.org/problem?id=2449

dijkstra算法中,一个点第一次从堆中出来的时候一定是最短路,第K次出来的时候一定是第k短路。

求解中可以使用A*设计估价函数使得结果朝着目标更快的收敛,本题设计的估计函数是该点到终点的最短路,因为这个最短路一定是小于目标函数的结果的,可以证明估计函数在这样的取值下一定能够收敛。

每次从堆中取出最优的点进行扩展,不断更新估计距离以及实际距离,最终一定能够快速收敛到答案。

注意求估价函数的过程是反图求最短路,多源而只有一个终点,可以直接对反图进行操作。另一个注意点就是,当终点和起点一致的时候就需要忽略长度为0的最短路,也就是选择k+1短路,

要使k++。

代码:

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
#define maxn 1005
#define maxm 100010
#define P pair<int,int>
#define PIII pair<int,pair<int,int>>
int head[maxn],nxt[maxm*2],rhead[maxn] ,len[maxm*2],ver[maxm*2];
int tot=0;
bool vis[maxn];
int d[maxn],cnt[maxn];
int st,ed,k,n,m;
void addedge(int* head,int u,int v,int w){
    ver[++tot]=v;
    len[tot]=w;
    nxt[tot]=head[u];
    head[u]=tot;
}
void dijkstra(int s){
    memset(vis,false,sizeof(vis));
    memset(d,0x3f,sizeof(d));
    d[s]=0;
    priority_queue<P,vector<P>,greater<P> > q;
    q.push(make_pair(0,s));
    while(!q.empty()){
        pair<int,int> cur=q.top();
        q.pop();
        int x=cur.second;
        if(vis[x])continue;
        vis[x]=1;
        for(int i=rhead[x];i;i=nxt[i]){
            int y=ver[i];
            int length=len[i];
            if(d[y]>d[x]+length)
            {
                d[y]=d[x]+length;
                q.push(make_pair(d[y],y));
            }
        }
    }
}
int A_star(){
    if(st==ed)++k;
    memset(cnt,0,sizeof(cnt));
    priority_queue<PIII,vector<PIII>,greater<PIII> > heap;
    heap.push(make_pair(d[st],make_pair(0,st)));//三元组,分别是估计值,真实距离和编号
    
    while(heap.size()){
        PIII now=heap.top();
        heap.pop();
        int x=now.second.second;
        int distance=now.second.first;//顶点和实际距离 
        if(cnt[x]==k)continue;
        cnt[x]++;
        if(x==ed && cnt[x]==k)return distance;
        for(int i=head[x];i;i=nxt[i]){
            int y=ver[i],length=len[i];
            if(cnt[y]<k){
                heap.push(make_pair(distance+length+d[y],make_pair(distance+length,y)));
            }
        }
    }
    return -1;
}
int main(){
    cin>>n>>m;
    int u,v,w;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        //保存原图以及反图 
        addedge(head,u,v,w);
        addedge(rhead,v,u,w);
    }
    
    cin>>st>>ed>>k;
    dijkstra(ed);
    cout<<A_star()<<endl;
}

 

posted @ 2020-06-21 11:34  WA自动机~  阅读(222)  评论(0编辑  收藏  举报