zoukankan      html  css  js  c++  java
  • 畅通工程续(Dijkstra算法)

    对Dijkstra算法不是很熟悉,写一下思路,希望通过写博客加深理解

    Description

    某省自从实行了很多年的畅通工程计划后,终于修建了很多路。不过路多了也不好,每次要从一个城镇到另一个城镇时,都有许多种道路方案可以选择,而某些方案要比另一些方案行走的距离要短很多。这让行人很困扰。 

    现在,已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离。
     

    Input

    本题目包含多组数据,请处理到文件结束。 
    每组数据第一行包含两个正整数N和M(0<N<200,0<M<1000),分别代表现有城镇的数目和已修建的道路的数目。城镇分别以0~N-1编号。 
    接下来是M行道路信息。每一行有三个整数A,B,X(0<=A,B<N,A!=B,0<X<10000),表示城镇A和城镇B之间有一条长度为X的双向道路。 
    再接下一行有两个整数S,T(0<=S,T<N),分别代表起点和终点。
     

    Output

    对于每组数据,请在一行里输出最短需要行走的距离。如果不存在从S到T的路线,就输出-1. 
     

    Sample Input

    3 3 0 1 1 0 2 3 1 2 1 0 2 3 1 0 1 1 1 2
     
    注意Dijkstra算法适用权值为非负的情况,此算法用于求一个节点到另一个节点的最短路径
    建立两个集合U和V,用vis[i] = 1;标记编号为i的点在集合U中,用vis[i] = 1标记在V中, 
    把已经找到最短路径的点放在集合U中(即为永久),没有找到的放在V中(称为临时点),
    当然dis[s] = 0, vis[s] = 1;也就是说s点到自己的最短路径为0,s点已经找到了离s的最短路径
    然后依次找其他各点到s的最短路径,依次把U中的点放在V中, 总保持从s到U中各点的最短路径小于从s到V中任意一点的最短路径
     
    假设要找从s点到t点的最短路径,最短路径为u1,u2,u3……un,那么先找到的是从s点到u(n-1)点的最短路径,以此类推
    步骤1:把任意两点的距离附为正无穷,
      2:输入u点到v点的距离w,如果u点到v点的距离大于w,那么更新u点到v点的距离为w
      3:把arr[s][i]赋给dis[i],同时把所有点标记为临时节点,然后dis[s] = 0,vis[s] = 1; 
        这样一来与s相邻的点到s的距离dis[i]就被赋值了,其他点到s点的距离则被附为正无穷
      4:找到所有临时点中离起点最近的点k,把这个点标记为永久,一共找n次
      5:更新所有临时节点到起点的距离,假设临时点为j,如果dis[j] > dis[k] + arr[k][j],就dis[j] = dis[k] + arr[k][j];
       即找出是经过k点到s点的距离与不经过k点到s点的距离中的最短距离
     
     
    代码1:
    #include <stdio.h>
    #define M 210
    #define INF 0x3f3f3f
    int arr[M][M], vis[M], dis[1010];
    int n, m;
    void Dijkstra(int src)
    {
        for(int i = 0; i < n; i++)
        {
            vis[i] = 0;
            dis[i] = arr[src][i];
        }
        int tmp, k;
        vis[src] = 1;
        dis[src] = 0;
        for(int i = 0; i < n; i++)
        {
            tmp = INF;
            for(int j = 0; j < n; j++)
            {
                if(!vis[j] && tmp > dis[j])
                {
                    tmp = dis[j];
                    k = j;
                }
            }
            vis[k] = 1;
            for(int j = 0; j < n; j++)
            {
                if(!vis[j] && dis[j] > arr[k][j] + dis[k])
                    dis[j] = arr[k][j] + dis[k];
            }
        }
    }
    int main()
    {
        int s, t, u, v, w;
        while(~scanf("%d%d", &n, &m))
        {
            for(int i = 0; i < n; i++)
            {
                for(int j = 0; j < n; j++)
                    arr[i][j] = INF;
            }
            for(int i = 0; i < m; i++)
            {
                scanf("%d%d%d", &u, &v, &w);
                if(arr[u][v] > w)
                    arr[u][v] = arr[v][u] = w;
            }
            scanf("%d%d", &s, &t);
            Dijkstra(s);
            if(dis[t] == INF)
                printf("-1
    ");
            else
                printf("%d
    ", dis[t]);
        }
        return 0;
    }

    上一个代码是我以前参考别人的代码写的,今天又看了这道题,自己写了代码

    刚开始一直wrong answer ,竟然一直找不到原因,后来发现我没有考虑从本城市到本城市的情况,初始化的时候arr[i][i] = 0

    因为这个,wrong answer 10发,真是醉了

    /*

        问题:假设所有城市编号0到n-1,给出城市的数目n,已经修的路的数目m,m行给出m条路中a城和b成的距离x

           输入出发城市s和目标城市t,问从出发城市到目标城市的最短路线

           如果没有路,输出-1

        分析:把出发的点s带入Dijkstra算法进行计算,算出所有点到src的最短距离,输出arr[src][t]即可

           代码块1:初始化

                1:把所有点之间的距离初始化为无穷,

                2:别忘了把自己到自己的距离初始化为0(因为这个原因wrong answer 10 发,真的够了)

                                      3:录入输入的城市a到城市b的距离x,如果x小于原本存储的arr[a][b](也就是a城市到b城市的距离)那么就更新a城市到b城市的距离 

           代码块2:Dijkstra算法:

                1:把所有的城市标记为临时的

                2:共有n个城市,先把出发的城市标记为永久的,因为每次把一个城市标记为临时的所以还要循环n-1次来标记其他的城市,直到所有城市都为永久

                3:每次的循环中找到所有城市中距离起点最近的城市k,并把这个城市标记为永久,更新其他所有城市,假设为"i"到到起点的距离为min(arr[s][k]+arr[k][i], arr[s][i]);

                这样就找到了所有城市距离起点s的最短距离,输出arr[s][t]即可

    */

     代码2:

    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    #define M 1000+10
    #define INF 0x3f3f3f
    using namespace std;
    int arr[M][M];
    bool vis[M];
    int n, m;
    void Init()
    {
        int a, b, x;
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
                arr[i][j] = INF;
        for(int i = 0; i < n; i++)
            arr[i][i] = 0;
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d%d", &a, &b, &x);
            if(x < arr[a][b])
                arr[a][b] = arr[b][a] = x;
        }
    }
    void Dijkstra(int src)
    {
        int tmp, k;
        memset(vis, 0, sizeof(vis));
        vis[src] = 1;
        arr[0][0] = 0;
        for(int i = 1; i < n; i++)
        {
            tmp = INF;
            for(int j = 0; j < n; j++)
            {
                if(arr[src][j] < tmp && !vis[j])
                {
                    tmp = arr[src][j];
                    k = j;
                }
            }
            vis[k] = 1; //把找到的那个点标记为永久
            for(int j = 0; j < n; j++)
            {
                if(arr[src][k]+arr[k][j] < arr[src][j])
                    arr[src][j] = arr[src][k] + arr[k][j];
            }
        }
    }
    int main()
    {
        int s, t;
        //freopen("input.txt", "r", stdin);
        while(~scanf("%d%d", &n, &m))
        {
            Init();
            scanf("%d%d", &s, &t);
            Dijkstra(s);
            if(arr[s][t] != INF)
                printf("%d
    ", arr[s][t]);
            else
                printf("-1
    ");
        }
        return 0;
    }
     
     
     
  • 相关阅读:
    F. Maximum White Subtree 树形dp*换根
    D
    E
    两圆相交板子
    lucass定理
    高精度求组合数
    康托展开与康托逆展开
    FFT变换
    Codeforces Round #625 Div. 1 Problem C
    E.Multiply Pollard_rho质因数分解
  • 原文地址:https://www.cnblogs.com/rain-1/p/4802546.html
Copyright © 2011-2022 走看看