题目链接: http://poj.org/problem?id=2449
题目大意: 在一个有N个点M条边的有向连通图里
找到S到T的第k短路的长度
解题思路: 经典的k短路A*算法题
估价函数: f[x]=h[x]+g[x]
f[x]: 估计经过该点的路线到达T点最小需要经过的路径长度 (大于或等于实际最短长度)
h[x]: 从起点到该点实际走的长度 (已经走了)
g[x]: 估计从该点到达终点经过的最小长度 (还没走的)
f[x]是实际走的,也就是BFS的当前路径长度
而g[x]可以用Dijkstra反向建边求终点到达其他所有点的最短距离
最小优先队列每次出队列的是f[x]最小的
BFS出队列代表走到这个点
第一次出队列是最短路,第二次出队列是次短路,第k次也就是k短路!!!
当S==T时,k++,为什么呢?因为第一次出队列的就是终点,显然只是在原地打转,没有实际的路线
代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #include <queue> using namespace std; #define INF 0x3f3f3f3f #define MAX 1001 typedef struct node{ int v,h,g,f; friend bool operator < (node a,node b) //最小优先队列 { return a.f>b.f; } }snode; struct ArcNode{ int to,weigh; struct ArcNode *next; }; struct ArcNode *listb[MAX]; //正向建边用邻接表解决重边问题 int n,m,dist[MAX],visit[MAX]={0},ant[MAX]={0},edge2[MAX][MAX]; void Dijkstra(int v0) //终点到其他所有点的最短距离,构造g[] { int i,j,min,minv; for(i=1;i<=n;i++) { edge2[i][i]=0; dist[i]=edge2[v0][i]; } for(i=1;i<=n;i++) { min=INF; minv=v0; for(j=1;j<=n;j++) { if(dist[j]<min&&!visit[j]) { min=dist[j]; minv=j; } } visit[minv]=1; for(j=1;j<=n;j++) { if(!visit[j]&&edge2[minv][j]<INF&&dist[minv]+edge2[minv][j]<dist[j]) { dist[j]=dist[minv]+edge2[minv][j]; } } } return ; } int Astar(int sv,int ev,int k) { snode v,tempv; struct ArcNode *temp; priority_queue<snode>q; v.v=sv; v.h=0; v.g=dist[sv]; v.f=v.h+v.g; //初始化 q.push(v); while(!q.empty()) { v=q.top(); q.pop(); ant[v.v]++; if(ant[v.v]>k) continue; //这个点访问次数超过k肯定不在这k条路上 if(ant[v.v]==k&&v.v==ev) return v.h; //第k次到达终点,返回实际走的长度 temp=listb[v.v]; while(temp!=NULL) { tempv.v=temp->to; //与之相连的点入队列 tempv.h=v.h+temp->weigh; //新点的h[x]=h[x']+xx' tempv.g=dist[temp->to]; //新点的g[x]=dist[x] tempv.f=tempv.h+tempv.g; //新点的f[x]=h[x]+g[x] q.push(tempv); //与之相连的点入队列 temp=temp->next; } } return -1; } int main() { int i,a,b,c; struct ArcNode *temp; scanf("%d%d",&n,&m); memset(edge2,INF,sizeof(edge2)); for(i=0;i<m;i++) { scanf("%d%d%d",&a,&b,&c); temp=(struct ArcNode*)malloc(sizeof(struct ArcNode)); //正向建边 temp->to=b; temp->weigh=c; temp->next=NULL; if(listb[a]==NULL) listb[a]=temp; else { temp->next=listb[a]; listb[a]=temp; } if(edge2[b][a]>c) //反向建边,重边取最小的边 edge2[b][a]=c; } scanf("%d%d%d",&a,&b,&c); if(a==b) //陷阱 c++; Dijkstra(b); printf("%d\n",Astar(a,b,c)); return 0; }注:原创文章,转载请注明出处