POJ---2449 Remmarguts' Date[K短路入门题(Dijkstra()||Spfa()+A*)]

Remmarguts' Date
Time Limit: 4000MS   Memory Limit: 65536K
Total Submissions: 15399   Accepted: 4202

Description

"Good man never makes girls wait or breaks an appointment!" said the mandarin duck father. Softly touching his little ducks' head, he told them a story.

"Prince Remmarguts lives in his kingdom UDF – United Delta of Freedom. One day their neighboring country sent them Princess Uyuw on a diplomatic mission."

"Erenow, the princess sent Remmarguts a letter, informing him that she would come to the hall and hold commercial talks with UDF if and only if the prince go and meet her via the K-th shortest path. (in fact, Uyuw does not want to come at all)"

Being interested in the trade development and such a lovely girl, Prince Remmarguts really became enamored. He needs you - the prime minister's help!

DETAILS: UDF's capital consists of N stations. The hall is numbered S, while the station numbered T denotes prince' current place. M muddy directed sideways connect some of the stations. Remmarguts' path to welcome the princess might include the same station twice or more than twice, even it is the station with number S or T. Different paths with same length will be considered disparate.

Input

The first line contains two integer numbers N and M (1 <= N <= 1000, 0 <= M <= 100000). Stations are numbered from 1 to N. Each of the following M lines contains three integer numbers A, B and T (1 <= A, B <= N, 1 <= T <= 100). It shows that there is a directed sideway from A-th station to B-th station with time T.

The last line consists of three integer numbers S, T and K (1 <= S, T <= N, 1 <= K <= 1000).

Output

A single line consisting of a single integer number: the length (time required) to welcome Princess Uyuw using the K-th shortest path. If K-th shortest path does not exist, you should output "-1" (without quotes) instead.

Sample Input

2 2

1 2 5

2 1 4

1 2 2

Sample Output

14

Source

POJ Monthly,Zeyuan Zhu
 
 
 
 
 
这道题的本质就是A*求K短路,题目的要求是求出s到t的第K短的路,可以用A*解 决,首先反向建图用Dijkstra求估价函数h[i],即i点到终点t的距离, 然后用基于BFS的优先队列A*来搜,当某个点第K次从队列中被pop出来的时候就找到答案了,严格的证明我也没想到,但可以给出简单直白的证明:
首 先有一个比较明显但很重要的事实,就是K短路中一定包含K条走向终点的路,而程序中的cnt[]数组所标记的正是最小的几条路线的条数,这个道理很明显。 当一个点被pop出来的时候,由于估价函数是这个点到终点的距离,所以一定是当前最优的走向终点的点,所以程序所走的每一步都是绝对最优的选择,这个有点 类似贪心,即用子问题的最优来推出最终的答案。另外一方面,任意一个点都不可能在这条K短路上出现K次以上,这个证明也比较直白,就是每次走到这个点时, 必然多出一条通向终点的不同路线,由于题目中说了路线不同算两条路,所以当第K次走到该点时,至少包含了K条走向终点的不同的路线,所以当搜到第K次走到 某个点时,已经没必要搜下去了,下个pop出来的点所包含的路线长度无论如何都不会比这条路线短,且这条路线已经被证明了包含至少K条不同的路线,故第K次走到当前点所花的代价再直接加上从该点到终点所花的代价即可
 
code:
  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 }

 

你可能感兴趣的:(dijkstra)