题目链接: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; }