NOIP--图论-第K短路

  所谓K短路,就是从s到t的第K短的路,第1短就是最短路。

    

如何求第K短呢?有一种简单的方法是广度优先搜索,记录t出队列的次数,当t第k次出队列时,就是第k短路了。但点数过大时,入队列的节点过多,时间和空间复杂度都较高。

    A*是在搜索中常用的优化,一种启发式搜索。简单的说,它可以用公式表示为f(n) = g(n) + f(n),其中,f(n)是从s经由节点n到t的估价函数,g(n)是在状态空间中从s到n的实际代价,h(n)是从n到t的最佳路径估计代价。在设计中,要保证h(n)<= n到t的实际代价,这一点很重要,h(n)越接近真实值,速度越快。

    由于启发函数的作用,使得计算机在进行状态转移时尽量避开不可能产生最优解的分支,而选择相对较接近最优解的路径进行搜索,降低了时间和空间复杂度。

    算法过程:

    1. 将图反向,用dijstra+heap求出t到所有点的最短距离,目的是求所有点到点t的最短路,用dis[i]表示i到t的最短路,其实这就是A*的启发函数,显然:h(n)<= n到t的实际代价。

    2. 定义估价函数。我们定义g(n)为从s到n所花费的代价,h(n)为dis[n],显然这符合A*算法的要求。

    3. 初始化状态。状态中存放当前到达的点i,fi,gi。显然,fi=gi+dis[i]。初始状态为(S,dis[S],0),存入优先级队列中。

    4. 状态转移。假设当前状态所在的点v相邻的点u,我们可以得到转换:(V,fv,gv)-->(U,fu+w[v][u],gv+w[v][u])。

    5. 终止条件。每个节点最多入队列K次,当t出队列K次时,即找到解。

 

    例:POJ2449

    题意:裸的K短路。题目大意就是给出一个图,然后给出一个起点个一个终点,求这两点间的第K短路。

Cpp代码

  1. #include <iostream>  

  2. #include <cstdio>  

  3. #include <cstring>  

  4. #include <queue>  

  5. using namespace std;  

  6. const int INF = 0x3f3f3f3f;  

  7. const int MAX = 1005;  

  8. int n,m;  

  9. int start,end,k;  

  10. struct Edge  

  11. {  

  12.     int w;  

  13.     int to;  

  14.     int next;  

  15. };  

  16. Edge e[100005];  

  17. int head[MAX],edgeNum;  

  18. int dis[MAX];   //dis[i]表示从i点到end的最短距离  

  19. bool vis[MAX];  

  20. int cnt[MAX];  

  21. vector<Edge> opp_Graph[MAX];  

  22.   

  23. struct Node  

  24. {  

  25.     int f,g;    //f = g+dis[v]  

  26.     int v;      //当前到达的节点  

  27.     Node(int a, int b,int c):f(a),g(b),v(c){}  

  28.     bool operator < (const Node& a) const  

  29.     {  

  30.         return a.f < f;  

  31.     }  

  32. };  

  33.   

  34. void addEdge(int from, int to, int w)  

  35. {  

  36.     e[edgeNum].to = to;  

  37.     e[edgeNum].w = w;  

  38.     e[edgeNum].next = head[from];  

  39.     head[from] = edgeNum++;  

  40. }  

  41.   

  42. void dijikastra(int start)  

  43. {  

  44.     int i;  

  45.     memset(vis,0,sizeof(vis));  

  46.     for(i = 1; i <= n; i++)  

  47.         dis[i] = INF;  

  48.     dis[start] = 0;  

  49.     priority_queue<Node> que;  

  50.     que.push(Node(0,0,start));  

  51.     Node next(0,0,0);  

  52.     while(!que.empty())  

  53.     {  

  54.         Node now = que.top();  

  55.         que.pop();  

  56.         if(vis[now.v])              //从集合T中选取具有最短距离的节点  

  57.             continue;  

  58.         vis[now.v] = true;          //标记节点已从集合T加入到集合S中  

  59.         for(i = 0; i < opp_Graph[now.v].size(); i++)    //更新从源点到其它节点(集合T中)的最短距离  

  60.         {  

  61.             Edge edge = opp_Graph[now.v][i];  

  62.             if(!vis[edge.to] && dis[now.v] + edge.w < dis[edge.to])     //加不加前面的判断无所谓  

  63.             {  

  64.                 dis[edge.to] = dis[now.v] + edge.w;  

  65.                 next.f = dis[edge.to];  

  66.                 next.v = edge.to;  

  67.                 que.push(next);  

  68.             }  

  69.         }  

  70.     }  

  71. }  

  72.   

  73. int A_Star()  

  74. {  

  75.     int i;  

  76.     priority_queue<Node> que;  

  77.     if(dis[start] == INF)  

  78.         return -1;  

  79.     que.push(Node(dis[start],0,start));  

  80.     Node next(0,0,0);  

  81.     while(!que.empty())  

  82.     {  

  83.         Node now = que.top();  

  84.         que.pop();  

  85.         cnt[now.v]++;  

  86.         if(cnt[end] == k)  

  87.             return now.f;  

  88.         if(cnt[now.v] > k)  

  89.             continue;  

  90.         for(i = head[now.v]; i != -1; i = e[i].next)  

  91.         {  

  92.             next.v = e[i].to;  

  93.             next.g = now.g + e[i].w;  

  94.             next.f = next.g + dis[e[i].to];  

  95.             que.push(next);  

  96.         }  

  97.     }  

  98.     return -1;  

  99. }  

  100.   

  101. int main()  

  102. {  

  103.     int i;  

  104.     int from,to,w;  

  105.     edgeNum = 0;  

  106.     memset(head,-1,sizeof(head));  

  107.     memset(opp_Graph,0,sizeof(opp_Graph));  

  108.     memset(cnt,0,sizeof(cnt));  

  109.     scanf("%d %d",&n,&m);  

  110.     Edge edge;  

  111.     for(i = 1; i <= m; i++)  

  112.     {  

  113.         scanf("%d %d %d",&from,&to,&w);  

  114.         addEdge(from,to,w);  

  115.         edge.to = from;  

  116.         edge.w = w;  

  117.         opp_Graph[to].push_back(edge);  

  118.     }  

  119.     scanf("%d %d %d",&start,&end,&k);  

  120.     if(start == end)  

  121.         k++;  

  122.     dijikastra(end);  

  123.     int result = A_Star();  

  124.     printf("%d\n",result);  

  125.     return 0;  

  126. }  

 

NOIP信息学视频地址

视频地址

链接:https://pan.baidu.com/s/1tHo1DFMaDuMZAemNH60dmw 
提取码:7jgr

posted @ 2020-11-05 13:38  tianli3151  阅读(93)  评论(0编辑  收藏  举报