Time Limit: 4000MS | Memory Limit: 65536K | |
Total Submissions: 15399 | Accepted: 4202 |
Description
Input
Output
Sample Input
2 2 1 2 5 2 1 4 1 2 2
Sample Output
14
Source
1 /* 2 所谓K短路,就是从s到t的第K短的路,第1短就是最短路。 3 如何求第K短呢?有一种简单的方法是广度优先搜索,记录t出队列的次数,当t第k次出队列时,就是第k短路了。但点数过大时,入队列的节点过多,时间和空间复杂度都较高。 4 A*是在搜索中常用的优化,一种启发式搜索。简单的说,它可以用公式表示为f(n) = g(n) + f(n),其中,f(n)是从s经由节点n到t的估价函数,g(n)是在状态空间中从s到n的实际代价, 5 h(n)是从n到t的最佳路径估计代价。在设计中,要保证h(n)<= n到t的实际代价,这一点很重要,h(n)越接近真实值,速度越快。 6 由于启发函数的作用,使得计算机在进行状态转移时尽量避开不可能产生最优解的分支,而选择相对较接近最优解的路径进行搜索,降低了时间和空间复杂度。 7 算法过程: 8 1. 将图反向,用dijstra+heap求出t到所有点的最短距离,目的是求所有点到点t的最短路,用dis[i]表示i到t的最短路,其实这就是A*的启发函数,显然:h(n)<= n到t的实际代价。 9 2. 定义估价函数。我们定义g(n)为从s到n所花费的代价,h(n)为dis[n],显然这符合A*算法的要求。 10 3. 初始化状态。状态中存放当前到达的点i,fi,gi。显然,fi=gi+dis[i]。初始状态为(S,dis[S],0),存入优先级队列中。 11 4. 状态转移。假设当前状态所在的点v相邻的点u,我们可以得到转换:(V,fv,gv)-->(U,fu+w[v][u],gv+w[v][u])。 12 5. 终止条件。每个节点最多入队列K次,当t出队列K次时,即找到解。 13 */ 14 15 #include<iostream> 16 #include<queue> 17 #include<vector> 18 using namespace std; 19 20 #define MAXN 1010 21 #define inf 0x3fffffff 22 23 int head[MAXN]; //邻接矩阵 24 int vst[MAXN]; //Dijkstra()中标记各点是否被访问 25 int dis[MAXN]; //Dijkstra()中各点到终点的距离 26 int cnt[MAXN]; //A*算法中记录出队列的次数 27 28 int n,m; 29 int st,et,k; 30 int edgenum; 31 32 33 typedef struct //边的结构体 34 { 35 int to; 36 int dis; 37 int next; 38 }Edge; 39 Edge e[100010]; 40 vector<Edge>G[MAXN]; 41 42 struct Node //节点的结构体 43 { 44 int f,g; //评估函数:f=g+dis[v],f代表路径的长度,g代表距离起点的距离,dis[v]代表距离终点的距离 45 int v; //当前到达的节点 46 Node(int a,int b,int c):f(a),g(b),v(c){} 47 bool operator < (const Node &a)const //优先队列,f越小说明越接近 48 { 49 return a.f<f; 50 } 51 }; 52 53 //邻接矩阵加边 54 void addEdge(int from,int to,int dis) 55 { 56 e[edgenum].to=to; 57 e[edgenum].dis=dis; 58 e[edgenum].next=head[from]; 59 head[from]=edgenum++; 60 } 61 62 //Dijkstra来求反向图中各点距离终点的最短路径 63 void Dijkstra(int start) 64 { 65 int i; 66 for(i=1;i<=n;i++) 67 { 68 vst[i]=0; 69 dis[i]=inf; 70 } 71 dis[start]=0; 72 priority_queue<Node>Q; 73 Q.push(Node(0,0,start)); //起点的f和g的值都为0,当前点v为start 74 Node next(0,0,0); 75 while(!Q.empty()) 76 { 77 Node now=Q.top(); 78 Q.pop(); 79 if(vst[now.v]) //如果该点已被访问过,则continue 80 continue; 81 vst[now.v]=1; 82 for(int i=0;i<G[now.v].size();i++) 83 { 84 Edge edge=G[now.v][i]; 85 if(!vst[edge.to]&&dis[now.v]+edge.dis<dis[edge.to]) //松弛 86 { 87 dis[edge.to]=dis[now.v]+edge.dis; 88 next.f=dis[edge.to]; 89 next.v=edge.to; 90 Q.push(next); 91 } 92 } 93 } 94 } 95 96 //A*算法 97 int A_star() 98 { 99 memset(cnt,0,sizeof(cnt)); //计数数组清零 100 int i; 101 priority_queue<Node>Q; //构建优先队列 102 if(dis[st]==inf) //如果起点到终点之间不存在通路(即起点无法到达终点) 103 return -1; 104 Q.push(Node(dis[st],0,st)); //评估函数:f=g+dis[v],由于从起点出发,故g置为0 105 Node next(0,0,0); 106 while(!Q.empty()) 107 { 108 Node now=Q.top(); 109 Q.pop(); 110 cnt[now.v]++; 111 if(cnt[et]==k) //如果终点的出队次数等于k,则说明已经找到了k短路,故返回f的值 112 return now.f; 113 if(cnt[now.v]>k) 114 continue; 115 for(i=head[now.v];i!=-1;i=e[i].next) 116 { 117 next.v=e[i].to; //下个节点 118 next.g=now.g+e[i].dis; 119 next.f=next.g+dis[e[i].to]; 120 Q.push(next); 121 } 122 } 123 return -1; //没有找到k短路,则输出-1 124 } 125 126 //主函数 127 int main() 128 { 129 int a,b,t; 130 edgenum=0; 131 memset(G,0,sizeof(G)); 132 memset(head,-1,sizeof(head)); 133 scanf("%d%d",&n,&m); 134 for(int i=0;i<m;i++) 135 { 136 Edge edge; 137 scanf("%d%d%d",&a,&b,&t); 138 addEdge(a,b,t); 139 edge.to=a; 140 edge.dis=t; 141 G[b].push_back(edge); 142 } 143 scanf("%d%d%d",&st,&et,&k); 144 if(st==et) //因为s既是源点又是汇点,所以其第一次入队的时候距离还为0 145 k++; 146 Dijkstra(et); 147 int ans=A_star(); 148 printf("%d\n",ans); 149 return 0; 150 }