• HDU2680Choose the best route(SPFA)


    题目的意思:

                     第一组输入3个数,n, m and s,(n<1000,m<20000,1=<s<=n) ,其中n为结点个数,m为边数,s为终点。

                     之后有m条输入,p , q , t (0<t<=1000),表示从点p到点q的时间为t.

                     最后输入一个数n,表示起点有n种可能。

    这道题目可以用Dijkstra,也是比较经典的题目,换换新的方式,改用SPFA试试。

    注意:这个图是单向的。

    Input
    There are several test cases.
    Each case begins with three integers n, m and s,(n<1000,m<20000,1=<s<=n) n stands for the number of bus stations in this city and m stands for the number of directed(定向) ways between bus stations .(Maybe there are several ways between two bus stations .) s stands for the bus station that near Kiki’s friend’s home.
    Then follow m lines ,each line contains three integers p , q , t (0<t<=1000). means from station p to station q there is a way and it will costs t minutes .
    Then a line with an integer w(0<w<n), means the number of stations Kiki can take at the beginning. Then follows w integers stands for these stations.
    这上面有一句话:directed(定向) ways between bus stations

    SPFA:

              建议看SPFA前先看看Dijkstra和Bellman-Ford这两个最短路算法。

    SPFA的思路比较简单,网上的说法也比较统一,NOCOW和百度百科上都有。这里在网上找到讲的比较通俗易懂的:

     

    SPFA(Shortest Path Faster Algorithm)
    是Bellman-Ford算法的一种队列实现,减少了不必要的冗余计算。
    算法大致流程是用一个队列来进行维护。 初始时将源加入队列。 每次从队列中取出一个元素,
    并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队。 直到队列为空时算法结束。

    它可以在O(kE)的时间复杂度内求出源点到其他所有点的最短路径,可以处理负边。

    SPFA 在形式上和BFS非常类似,不同的是BFS中一个点出了队列就不可能重新进入队列,但是SPFA中
    一个点可能在出队列之后再次被放入队列,也就是一个点改进过其它的点之后,过了一段时间可能本
    身被改进,于是再次用来改进其它的点,这样反复迭代下去。

    判断有无负环:如果某个点进入队列的次数超过V次则存在负环(SPFA无法处理带负环的图)。(这个是SPFA的NB之处)

     

    思路:

           这道题一开始我是想用常规的方法,就是将后面可能出现的起点一个一个慢慢调用一次,然后比较求出最小路径。但是发现这样的方法会TLE,所以后来改为从终点往起点处扫。就是存边的时候头尾倒过来存,然后只调用一次SPFA,就可以保存所有起点的最短路径。改进后的程序只调用了SPFA函数一次。所以速度会快了很多。

    ps:

    1、单向还是双向图,考虑清楚
    2、注意那个maxInt这个值超不超,够不够
    3、注意是否两点间有多条路径
    4、分清变量是double型的还是int型的
    5、注意主函数中初始化map[][]中的点边不要搞错(注意所有初始化,正确命名好变量)

    #include<iostream> 
    using namespace std; 
     
    const int maxNum = 1010
    const int maxInt = 999999999
     
    int map[maxNum][maxNum]; 
    int dis[maxNum]; 
    char vst[maxNum]; 
     
    int nodeNum, edgeNum, start, result; 
     
    void SPFA(int start) 

        int i, pri, rear, p, Q[20*maxNum]; //注意队列这里的大小,因为每个点都能够入队在10次以内 
        memset(vst, 0sizeof(vst)); 
     
        for( i = 1; i <= maxNum; i++) 
            Q[i] = 0
     
        for(i = 1; i <= nodeNum; i++) 
            dis[i] = maxInt; 
     
        dis[start] = 0
        vst[start] = 1
        Q[0] = start; 
        pri = 0
        rear = 1
     
        while(pri < rear) 
        { 
            p = Q[pri]; 
            for(i = 1; i <= nodeNum; i++) 
            { 
                if(dis[p] + map[p][i] < dis[i]) 
                { 
                    dis[i] = dis[p] + map[p][i]; 
                    if(!vst[i]) 
                    { 
                        Q[rear++] = i; 
                        vst[i] = 1
                    } 
                } 
            } 
            vst[p] = 0
            pri++; 
        } 
        return ; 

     
    int main(void

        int i, s, e, w, j, min, nn, ans[maxNum]; 
        while(scanf("%d%d%d", &nodeNum, &edgeNum, &result) == 3
        { 
            for(i = 1; i <= nodeNum; i++) 
                for(j = 1; j <= nodeNum; j++) 
                { 
                    map[i][j] = maxInt; 
                } 
     
            for(i = 1; i <= edgeNum; i++) 
            { 
                scanf("%d%d%d", &s, &e, &w); 
                if(map[e][s] > w) 
                    map[e][s] = w; //注意这个图是单向的,这里有个小技巧,头尾颠倒处理。 
            } 
            SPFA(result);//把这个放外面,头尾颠倒,调用一次函数就可以了。如果按照一般的想法放在循环内部调用,那么会TLE 
            scanf("%d", &nn); 
            min = maxInt; 
            for(i = 0; i < nn; i++) 
            { 
                scanf("%d", &start); 
                if(min > dis[start]) 
                    min = dis[start]; 
            } 
            if(min == maxInt) 
            { 
                printf("-1\n"); 
            } 
            else 
            { 
                printf("%d\n", min); 
            } 
        } 
        return 0

  • 相关阅读:
    [Swift]LeetCode156.二叉树的上下颠倒 $ Binary Tree Upside Down
    [Swift]LeetCode155. 最小栈 | Min Stack
    [Swift]LeetCode154. 寻找旋转排序数组中的最小值 II | Find Minimum in Rotated Sorted Array II
    CXF生成client注意事项
    使用oracle10g官方文档找到监听文件(listener.ora)的模板
    在Vi里面实现字符串的批量替换
    C语言之基本算法12—谁是冠军
    虚拟机 minimal 安装增强包
    ZOJ
    leetcode Reverse Nodes in k-Group
  • 原文地址:https://www.cnblogs.com/cchun/p/2520131.html
Copyright © 2020-2023  润新知