• 寒假Day7:最短路


    HDU2112-HDU Today-dijkstra

    题面:

    经过锦囊相助,海东集团终于度过了危机,从此,HDU的发展就一直顺风顺水,到了2050年,集团已经相当规模了,据说进入了钱江肉丝经济开发区500强。这时候,XHD夫妇也退居了二线,并在风景秀美的诸暨市浬浦镇陶姚村买了个房子,开始安度晚年了。
    这样住了一段时间,徐总对当地的交通还是不太了解。有时很郁闷,想去一个地方又不知道应该乘什么公交车,在什么地方转车,在什么地方下车(其实徐总自己有车,却一定要与民同乐,这就是徐总的性格)。
    徐总经常会问蹩脚的英文问路:“Can you help me?”。看着他那迷茫而又无助的眼神,热心的你能帮帮他吗?
    请帮助他用最短的时间到达目的地(假设每一路公交车都只在起点站和终点站停,而且随时都会开)。
    Input
    输入数据有多组,每组的第一行是公交车的总数N(0<=N<=10000);
    第二行有徐总的所在地start,他的目的地end;
    接着有n行,每行有站名s,站名e,以及从s到e的时间整数t(0<t<100)(每个地名是一个长度不超过30的字符串)。
    note:一组数据中地名数不会超过150个。
    如果N==-1,表示输入结束。
    Output
    如果徐总能到达目的地,输出最短的时间;否则,输出“-1”。
    View Code

    样例:

    Sample Input
    6
    xiasha westlake
    xiasha station 60
    xiasha ShoppingCenterofHangZhou 30
    station westlake 20
    ShoppingCenterofHangZhou supermarket 10
    xiasha supermarket 50
    supermarket westlake 10
    -1
    
    Sample Output
    50
    
    
    Hint:
    The best route is:
    xiasha->ShoppingCenterofHangZhou->supermarket->westlake

    题意:问给定的起点到终点的最短时间。

    思路:dijkstra模板

    注意:坑比较多

    • 起点和终点可能是同一个点
    • 双向边且取最小值
    • 有map的应用,用于给定的起点和终点都是字符串,给定经过的点也是以字符串的形式存储
    sum=0;
    string st,en;
    cin>>st>>en;
    mp[st]=++sum;
    mp[en]=++sum;
    for(int i=1; i<=n; i++)
    {
        string ss,ee;
        int t;
        cin>>ss>>ee>>t;
        if(!mp[ss])
            mp[ss]=++sum;
        if(!mp[ee])
            mp[ee]=++sum;
        if(e[mp[ss]][mp[ee]]>t||e[mp[ee]][mp[ss]]>t)
            e[mp[ss]][mp[ee]]=e[mp[ee]][mp[ss]]=t;
    }

    顺带回顾一下dijkstra

     1 void dijkstra()
     2 {
     3     for(int i=1;i<=sum;i++)
     4     {
     5         dist[i]=e[1][i];
     6         book[i]=0;
     7     }
     8     book[1]=1,dist[1]=0;
     9     int u;
    10     for(int i=2;i<=sum;i++)
    11     {
    12         int minn=inf;
    13         for(int j=1;j<=sum;j++)
    14         {
    15             if(book[j]==0&&minn>dist[j])
    16             {
    17                 u=j;
    18                 minn=dist[j];
    19             }
    20         }
    21         if(minn==inf)
    22             break;
    23         book[u]=1;
    24         for(int j=1;j<=sum;j++)
    25         {
    26             if(book[j]==0&&dist[u][j]<inf)
    27             {
    28                 if(dist[u]+e[u][j]<dist[j])
    29                     dist[j]=dist[u]+e[u][j];
    30             }
    31         }
    32     }
    33 
    34 }
    View Code

    AC代码如下:

      1 #include<stdio.h>
      2 #include<string.h>
      3 #include<iostream>
      4 #include<algorithm>
      5 #include<queue>
      6 #include<map>
      7 #include<cmath>
      8 using namespace std;
      9 #define inf 0x3f3f3f3f
     10 const int N=1e4+20;
     11 
     12 int e[N][N],dist[N];
     13 int sum;
     14 bool book[N];
     15 map<string,int>mp;
     16 
     17 void init()
     18 {
     19     for(int i=1;i<=150;i++)
     20     {
     21         for(int j=1;j<=150;j++)
     22         {
     23             if(i==j)
     24                 e[i][j]=0;
     25             else
     26                 e[i][j]=inf;
     27         }
     28     }
     29 }
     30 
     31 void dijkstra()
     32 {
     33     for(int i=1;i<=sum;i++)
     34     {
     35         dist[i]=e[1][i];
     36         book[i]=0;
     37     }
     38     book[1]=1,dist[1]=0;
     39     int u;
     40     for(int i=2;i<=sum;i++)
     41     {
     42         int minn=inf;
     43         for(int j=1;j<=sum;j++)
     44         {
     45             if(book[j]==0&&minn>dist[j])
     46             {
     47                 u=j;
     48                 minn=dist[j];
     49             }
     50         }
     51         if(minn==inf)
     52             break;
     53         book[u]=1;
     54         for(int j=1;j<=sum;j++)
     55         {
     56             if(book[j]==0)
     57             {
     58                 dist[j]=min(dist[j],dist[u]+e[u][j]);
     59             }
     60         }
     61     }
     62 
     63 }
     64 
     65 int main()
     66 {
     67     ios::sync_with_stdio(false);
     68     int n;
     69     while(cin>>n)
     70     {
     71         if(n==-1)
     72             break;
     73         mp.clear();
     74         init();
     75         sum=0;
     76         string st,en;
     77         cin>>st>>en;
     78         mp[st]=++sum;
     79         mp[en]=++sum;
     80         for(int i=1;i<=n;i++)
     81         {
     82             string ss,ee;
     83             int t;
     84             cin>>ss>>ee>>t;
     85             if(!mp[ss])
     86                 mp[ss]=++sum;
     87             if(!mp[ee])
     88                 mp[ee]=++sum;
     89             if(e[mp[ss]][mp[ee]]>t||e[mp[ee]][mp[ss]]>t)
     90                 e[mp[ss]][mp[ee]]=e[mp[ee]][mp[ss]]=t;
     91         }
     92         //cout<<"---"<<sum<<endl;
     93         dijkstra();
     94         if(st==en)
     95         {
     96             cout<<0<<endl;
     97             continue;
     98         }
     99         //int w=e[1][2];
    100         int w=dist[2];
    101         if(w!=inf)
    102             cout<<w<<endl;
    103         else
    104             cout<<"-1"<<endl;
    105     }
    106     return 0;
    107 }
    View Code

          

    HDU6714-最短路2-Floyd

    • 法一:一种是用dijkstra写的,但是我没看懂。。。

                         参考博客:https://blog.csdn.net/birdmanqin/article/details/100068433

                                           https://www.cnblogs.com/iat14/p/11408298.html

    • 法二:一种用floyd写的(以下思路和代码是用floyd写的)

    题面:

    小 A 是社团里的工具人,有一天他的朋友给了他一个 n 个点,m 条边的正权连通无向图,要他计算所有点两两之间的最短路。
    
    作为一个工具人,小 A 熟练掌握着 floyd 算法,设 w[i][j] 为原图中 (i,j) 之间的权值最小的边的权值,若没有边则 w[i][j]=无穷大。特别地,若 i=j,则 w[i][j]=0。
    
    Floyd 的 C++ 实现如下:
    
    ```c++
    for(int k=1;k<=p;k++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
      w[i][j]=min(w[i][j],w[i][k]+w[k][j]);
    ```
    
    当 p=n 时,该代码就是我们所熟知的 floyd,然而小 A 为了让代码跑的更快点,所以想减少 p 的值。
    
    令 Di,j 为最小的非负整数 x,满足当 p=x 时,点 i 与点 j 之间的最短路被正确计算了。
    
    现在你需要求 ∑ni=1∑nj=1Di,j,虽然答案不会很大,但为了显得本题像个计数题,你还是需要将答案对 998244353 取模后输出。
    Input
    第一行一个正整数 T(T≤30) 表示数据组数
    
    对于每组数据:
    
    第一行两个正整数 n,m(1≤n≤1000,m≤2000),表示点数和边数。
    
    保证最多只有 5 组数据满足 max(n,m)>200
    
    接下来 m 行,每行三个正整数 u,v,w 描述一条边权为 w 的边 (u,v),其中 1≤w≤109
    Output
    输出 T 行,第 i 行一个非负整数表示第 i 组数据的答案
    
    Sample Input
    1
    4 4
    1 2 1
    2 3 1
    3 4 1
    4 1 1
    
    Sample Output
    6
    View Code

    题意:求所有顶点N中任意两点最短路中松弛点中最小值的和

    注意:

    • 用floyd很容易超时,这个地方处理很好。但是得放在第二层循环下,放在第三层会超时;
    if(e[i][k]>inff)
        continue;
    • 虽然每条边给的范围是109,但是由于floyd中会进行边的松弛,也就是会进行相加,所以需要开数组为ll;
    • 因为大部分数据开的为ll,所以在对于边的处理应该初始化为ll型的无穷大,也就是0x3f3f3f3f3f3f3f3f(8个3f),约19位;
    • 在处理数据的时候,第三层for循环到 j<i 即可,之后的结果*2,因为是双向边,所以后面的和前面一样也就无需重复处理了。

    代码如下:

     1 #include<stdio.h>
     2 #include<string.h>
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<queue>
     6 #include<vector>
     7 #include<map>
     8 #include<cmath>
     9 using namespace std;
    10 #define inf 0x3f3f3f3f
    11 #define inff 0x3f3f3f3f3f3f3f3f
    12 const int N=1010;
    13 #define mod 998244353
    14 typedef long long ll;
    15 
    16 ll e[N][N],dist[N],a[N][N];
    17 int n,m;
    18 bool book[N];
    19 map<string,int>mp;
    20 
    21 void init()
    22 {
    23     for(int i=1;i<=n;i++)
    24     {
    25         for(int j=1;j<=n;j++)
    26         {
    27             if(i==j)
    28                 e[i][j]=0;
    29             else
    30                 e[i][j]=inff;
    31         }
    32     }
    33 }
    34 
    35 int main()
    36 {
    37     ios::sync_with_stdio(false);
    38     int t;
    39     cin>>t;
    40     while(t--)
    41     {
    42         cin>>n>>m;
    43         memset(a,0,sizeof(a));
    44         init();
    45         for(int i=1;i<=m;i++)
    46         {
    47             ll u,v,w;
    48             cin>>u>>v>>w;
    49             e[u][v]=e[v][u]=min(e[u][v],w);
    50         }
    51         ll ans=0;
    52         for(int k=1;k<=n;k++)
    53         {
    54             for(int i=1;i<=n;i++)
    55             {
    56                 if(e[i][k]>inff)
    57                         continue;
    58                 for(int j=1;j<i;j++)
    59                 {
    60                     if(e[i][j]>e[i][k]+e[k][j])
    61                     {
    62                         ans+=k;
    63                         ans-=a[i][j];
    64                         a[i][j]=a[j][i]=k;
    65                         e[i][j]=e[j][i]=e[i][k]+e[k][j];
    66                     }
    67                 }
    68             }
    69         }
    70         //i:4 j:2 k:1
    71         //i:3 j:1 k:2
    72         ll w=ans*2%mod;
    73         cout<<w<<endl;
    74     }
    75     return 0;
    76 }

    小知识点回顾:

    • 0x3f3f3f3f3f3f3f3f 代表针对longlong表示的无穷大,约19位
    • dijkstra:时间复杂度O(n2),一般求单源最短路

                             针对无负权边的图,有向无向都可以

                             存储结构上比较占空间,因为用的是邻接矩阵。

    • floyd:适用于N比较小的情况。

                          注意模板上k是写在最外层循环。

    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
            }
        }
    }
    • SPFA:不能处理负权回路;
    • 最短路在处理初始边的时候记得判断是否是双向边,是否需要取最小值;
    • 问起点到终点距离问题的时候,有时需要考虑一下起点是不是就是终点;
    • 记得对边进行初始化

     TO DO LIST

    • w 50+
    • 复习了最短路floyd/dijkstra

     待解决问题:

    • 为什么floyd的含k循环写在最外层? --> 枚举的松弛点
    • HDU6714 代码理解的不是很透彻,不确定自己理解的是否正确,需要推理出结果。???
  • 相关阅读:
    BZOJ3689: 异或之
    BZOJ3166: [Heoi2013]Alo
    BZOJ3261: 最大异或和
    BZOJ3123: [Sdoi2013]森林
    BZOJ4520: [Cqoi2016]K远点对
    BZOJ2989: 数列
    BZOJ1941: [Sdoi2010]Hide and Seek
    BZOJ4066: 简单题
    BZOJ2648: SJY摆棋子
    KDtree模板(HDU4347)
  • 原文地址:https://www.cnblogs.com/OFSHK/p/12219824.html
Copyright © 2020-2023  润新知