zoukankan      html  css  js  c++  java
  • poj3259(spfa)

    自己的第一道spfa,纪念一下,顺便转载一下spfa的原理。先po代码:

    #include <iostream>
    #include <queue>
    using namespace std;
    
    const int MAX = 999999;
    const int MAXN = 501;
    
    int minimum(int a, int b){
        return a > b ? b : a;
    }
    
    int main()
    {
        int t;
        cin >> t;
        while (t--){
            int n, m, w;
            cin >> n >> m >> w;
            int field[MAXN];
            bool visited[MAXN];
            int edge[MAXN][MAXN];
            int visitCnt[MAXN];
            memset(field, MAX, sizeof(field));
            memset(edge, MAX, sizeof(edge));
            memset(visited, 0, sizeof(visited));
            memset(visitCnt, 0, sizeof(visitCnt));
            for (int i = 0; i < m; i++){
                int field1, field2, len;
                cin >> field1 >> field2 >> len;
                edge[field1][field2] = minimum(edge[field1][field2], len);
                edge[field2][field1] = minimum(edge[field2][field1], len);
            }
            for (int i = 0; i < w; i++){
                int field1, field2, len;
                cin >> field1 >> field2 >> len;
                edge[field1][field2] = minimum(edge[field1][field2], (-1) * len);
            }
            field[1] = 0;
            queue<int> Q;
            Q.push(1);
            visited[1] = true;
            visitCnt[1] = 1;
            bool flag = false;
            while (!Q.empty()){
                int current = Q.front();
                Q.pop();
                visited[current] = false;
                for (int i = 1; i <= n; i++){
                    int tmp = field[current] + edge[current][i];
                    if (tmp < field[i]){
                        field[i] = tmp;
                        if (!visited[i]){
                            Q.push(i);
                            visited[i] = true;
                            visitCnt[i]++;
                            if (visitCnt[i] > n){
                                flag = true;
                                break;
                            }
                        }
                    }
                }
                if (flag)
                    break;
            }
            if (flag){
                cout << "YES" << endl;
            }
            else{
                cout << "NO" << endl;
            }
        }
        return 0;
    }

    以下内容转自http://www.cnblogs.com/zgmf_x20a/archive/2008/12/18/1357737.html

    求最短路径的算法有许多种,除了排序外,恐怕是OI界中解决同一类问题算法最多的了。最熟悉的无疑是Dijkstra,接着是Bellman-Ford,它们都可以求出由一个源点向其他各点的最短路径;如果我们想要求出每一对顶点之间的最短路径的话,还可以用Floyd-Warshall。

    SPFA是这篇日志要写的一种算法,它的性能非常好,代码实现也并不复杂。特别是当图的规模大,用邻接矩阵存不下的时候,用SPFA则可以很方便地面对临接表。每个人都写过广搜,SPFA的实现和广搜非常相似。

    如何求得最短路径的长度值?

    首先说明,SPFA是一种单源最短路径算法,所以以下所说的“某点的最短路径长度”,指的是“某点到源点的最短路径长度”。

    我们记源点为S,由源点到达点i的“当前最短路径”为D[i],开始时将所有D[i]初始化为无穷大,D[S]则初始化为0。算法所要做的,就是在运行过程中,不断尝试减小D[]数组的元素,最终将其中每一个元素减小到实际的最短路径。

    过程中,我们要维护一个队列,开始时将源点置于队首,然后反复进行这样的操作,直到队列为空:

    (1)从队首取出一个结点u,扫描所有由u结点可以一步到达的结点,具体的扫描过程,随存储方式的不同而不同;

    (2)一旦发现有这样一个结点,记为v,满足D[v] > D[u] + w(u, v),则将D[v]的值减小,减小到和D[u] + w(u, v)相等。其中,w(u, v)为图中的边u-v的长度,由于u-v必相邻,所以这个长度一定已知(不然我们得到的也不叫一个完整的图);这种操作叫做松弛。

    松弛操作的原理是著名的定理:“三角形两边之和大于第三边”,在信息学中我们叫它三角不等式。所谓对i,j进行松弛,就是判定是否d[j]>d[i]+w[i,j],如果该式成立则将d[j]减小到d[i]+w[i,j],否则不动。


    (3)上一步中,我们认为我们“改进了”结点v的最短路径,结点v的当前路径长度D[v]相比于以前减小了一些,于是,与v相连的一些结点的路径长度可能会相应地减小。注意,是可能,而不是一定。但即使如此,我们仍然要将v加入到队列中等待处理,以保证这些结点的路径值在算法结束时被降至最优。当然,如果连接至v的边较多,算法运行中,结点v的路径长度可能会多次被改进,如果我们因此而将v加入队列多次,后续的工作无疑是冗余的。这样,就需要我们维护一个bool数组Inqueue[],来记录每一个结点是否已经在队列中。我们仅将尚未加入队列的点加入队列。


    算法能否结束?

    对于不存在负权回路的图来说,上述算法是一定会结束的。因为算法在反复优化各个最短路径长度,总有一个时刻会进入“无法再优化”的局面,此时一旦队列读空,算法就结束了。然而,如果图中存在一条权值为负的回路,就糟糕了,算法会在其上反复运行,通过“绕圈”来无休止地试图减小某些相关点的最短路径值。假如我们不能保证图中没有负权回路,一种“结束条件”是必要的。这种结束条件是什么呢?

    思考Bellman-Ford算法,它是如何结束的?显然,最朴素的Bellman-Ford算法不管循环过程中发生了什么,一概要循环|V|-1遍才肯结束。凭直觉我们可以感到,SPFA算法“更聪明一些”,就是说我们可以猜测,假如在SPFA中,一个点进入队列——或者说一个点被处理——超过了|V|次,那么就可以断定图中存在负权回路了。


    最短路径本身怎么输出?

    在一幅图中,我们仅仅知道结点A到结点E的最短路径长度是73,有时候意义不大。这附图如果是地图的模型的话,在算出最短路径长度后,我们总要说明“怎么走”才算真正解决了问题。如何在计算过程中记录下来最短路径是怎么走的,并在最后将它输出呢?

    Path[]数组,Path[i]表示从S到i的最短路径中,结点i之前的结点的编号。注意,是“之前”,不是“之后”。最短路径算法的核心思想成为“松弛”,原理是三角形不等式,方法是上文已经提及的。我们只需要在借助结点u对结点v进行松弛的同时,标记下Path[v] = u,记录的工作就完成了。

    输出时可能会遇到一点难处,我们记的是每个点“前面的”点是什么,输出却要从最前面往最后面输,这不好办。其实很好办,见如下递归方法:

    void PrintPath(int k){
        if( Path[k] ) PrintPath(Path[k]);
        fout<<k<<' ';
    }
  • 相关阅读:
    494. Target Sum 添加标点符号求和
    636. Exclusive Time of Functions 进程的执行时间
    714. Best Time to Buy and Sell Stock with Transaction Fee有交易费的买卖股票
    377. Combination Sum IV 返回符合目标和的组数
    325. Maximum Size Subarray Sum Equals k 和等于k的最长子数组
    275. H-Index II 递增排序后的论文引用量
    274. H-Index论文引用量
    RabbitMQ学习之HelloWorld(1)
    java之struts2的数据处理
    java之struts2的action的创建方式
  • 原文地址:https://www.cnblogs.com/caiminfeng/p/4876007.html
Copyright © 2011-2022 走看看