zoukankan      html  css  js  c++  java
  • 最短路模板

    dij

    //二叉堆优化过的dij
    #include <cmath>
    #include <queue>
    #include <string>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define forn(i, n) for (int i = 0; i < (n); i++)
    #define forab(i, a, b) for (int i = (a); i <= (b); i++)
    #define forba(i, b, a) for (int i = (b); i >= (a); i--)
    #define mset(a, n) memset(a, n, sizeof(a))
    #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
    #define P pair<int,int>
    #define fi first
    #define se second
    using namespace std;
    #define N 1000010
    #define maxn 1005
    #define inf 0x3f3f3f3f
    #define ll long long
    int head[N], ver[N], edge[N], Next[N], d[N];
    int n, m, tot;
    bool vis[N];
    priority_queue<pair<int,int> > q;  //first存的d[i]的相反数,这样就变成了小根堆 
    void addedge(int x,int y,int z)
    {
        ver[++tot] = y;
        edge[tot] = z;
        Next[tot] = head[x];
        head[x] = tot;
    }
    void Init()
    {
    	mset(head,0);
    	tot=0;
    }
    void dij()
    {
    	mset(d,0x3f);
    	mset(vis,0);
        d[1] = 0; //以1 为起始点,
        q.push(make_pair(0, 1));
        while(q.size()) 
    	{
            int x = q.top().se;
            q.pop();
            if(vis[x])
                continue;
            vis[x] = 1;
            for (int i = head[x]; i;i=Next[i])
            {
                int y = ver[i];
                int z = edge[i];
                if(d[y]>d[x]+z)
                {
                    d[y] = d[x] + z; //更新
                    q.push(make_pair(-d[y], y));
                }
            }
        }
    }
    int main()
    {
        fast;
        cin >> n >> m;
        forab(i,1,m)
        {
            int x, y, z;
            cin >> x >> y >> z;
            addedge(x, y, z);
            //addedge(y,x,z);  //无向边
        }
        dij();
        forab(i, 1, n) cout << d[i] << endl;  //单源最短路
        system("pause");
    }
    

      SPFA

    #include <cmath>
    #include <queue>
    #include <string>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define forn(i, n) for (int i = 0; i < (n); i++)
    #define forab(i, a, b) for (int i = (a); i <= (b); i++)
    #define forba(i, b, a) for (int i = (b); i >= (a); i--)
    #define mset(a, n) memset(a, n, sizeof(a))
    #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
    #define P pair<int,int>
    #define fi first
    #define se second
    using namespace std;
    #define N 1000010
    #define maxn 1005
    #define inf 0x3f3f3f3f
    #define ll long long
    int head[N], ver[N], edge[N], Next[N], d[N];
    int n, m, tot;
    bool vis[N];
    int in[N];  //判负环可用
    void addedge(int x,int y,int z)
    {
        ver[++tot] = y;
        edge[tot] = z;
        Next[tot] = head[x];
        head[x] = tot;
    }
    void Init()
    {
    	mset(head,0);
    	tot=0;
    }
    void spfa()
    {
        mset(d, 0x3f);
        mset(vis, 0);   //这里的vis跟dij的有所不同,spfa的vis是记录点是否在队列里面,所以要每次进出更新
        d[1] = 0;
        vis[1] = 1;
        q.push(1);
        while(q.size())
        {
            int x = q.front();
            q.pop();
            vis[x] = 0;  //x出队列
            for (int i = head[x]; i;i=Next[i])
            {
                int y = ver[i];
                int z = edge[i];
                if(d[y]>d[x]+z)
                {
                    d[y] = d[x] + z;
                    if(!vis[y])
                    {
                        vis[y] = 1;
                        q.push(y);
                    }
                }
            }
        }
    }
    int main()
    {
        fast;
        cin >> n >> m;
        forab(i,1,m)
        {
            int x, y, z;
            cin >> x >> y >> z;
            addedge(x, y, z);
            //addedge(y, x, z);  无向边
        }
        spfa();
        forab(i, 1, n) cout << d[i] << endl;
    }
    

      

    负环和差分约束
    之前写程序设计作业的时候就了解过spfa和差分约束,这里再解释一下

    负环: 一个边权和为复数的环称为负环。
    不难知道,如果图中存在负环,那么无论经过多少次迭代,总存在有向边(x,y,z)使得
    dist[y] > dist[x] + z则spfa算法无法结束。

    根据抽屉原理(不懂自行百度,我记着小学奥数就讲过= =),若存在一个dist[x] 从起点1到节点x的最短路包含>=n条边,则这条路径必然重复经过的某个结点p,也就是说这条最短路上存在一个环,环上的每个点都能更新下一个点的dist,p绕环一圈最后可以更新他自己,因此,整个环的总长度为负数,每绕一圈总长度就会减少一点,越来越少,不可能收敛到每条边都满足三角不等式的状态。

    因此 有以下方法判定法则(有了spfa就不用bellman-ford了吧,我是只看了spfa)

    第一种 按照上段文章所写,设cnt[x] 表示从1到x的最短路径包含的边数,cnt[1] = 0,当执行dist[y] = dist[x] + z 时,同样更新cnt[y] = cnt[x] + 1,此时若有cnt[y] >=n 则存在负环,若正常结束算法,则没有负环

    第二种 记录每个点的入队次数 次数达到n时则有负环,不过一般不如第一种的效率高。

    差分约束 另一篇文章里面给过一些详细的证明这里就不写了。

  • 相关阅读:
    Layui 两个table 人员选择 多选
    iphone7 忘记密码 重装系统
    使用hql-统计连续登陆的三天及以上的用户
    azkaban群起/群停脚本
    Hive读取索引文件问题:select * 和select count(*)读取出来的行数不一致
    scala/java等其他语言从CSV文件中读取数据,使用逗号','分割可能会出现的问题
    hadoop3.1.3版本的secondaryNamenode的web界面不能显示的问题?
    启动kafka消费报错:WARN [Consumer clientId…] 1 partitions have leader brokers without a matching listener,…
    centos6和centos7的防火墙命令,以及它们的区别是是什么?
    kafka项目经验之如何进行Kafka压力测试、如何计算Kafka分区数、如何确定Kaftka集群机器数量
  • 原文地址:https://www.cnblogs.com/zssst/p/11337852.html
Copyright © 2011-2022 走看看