【最短路问题】(单源,多源)

我与影子孤独终老i 提交于 2020-01-19 20:12:00

•单源最短路径

       No.1 dijkstra算法

             算法核心:(1)两个集合 s,t 分别保存已经更新过的和未更新的数据,用一个vis[]数组来实现;

                                (2)用dis[i]数组保存当前 i 点到源点的最短路径长度,每次在dis中找到一个最小的值dis[i],每次由dis[i] 更新与 i 相连的dis值;

                                                   p.s:dis[x]=0,x为源点;

                                   *(3)* 堆优化:每次找dis最小值时会花费大量时间,于是可以动用c++stl里的结构 大根堆(优先队列)实现优化

             时间复杂度:进行堆优化后的程序为 O(m*logn)

堆优化后code:         q为大根堆。

 1 void dijkstra(){
 2     memset(d,0x3f,sizeof(d));
 3     memset(v,0,sizeof(v));
 4     d[1]=0;
 5     q.push(make_pair(0,1));
 6     while(q.size()){
 7         int x=q.top().second;
 8         q,pop();
 9         if(v[x]) continue;
10         v[x]=1;
11         for(int i=head[x];i;i=next[i]){
12             int y=ver[i],z=edge[i];
13             if(d[y]>d[x]+z){
14                 d[y]=d[x]+z;
15                 q.push(make_pair(-d[y],y));
16             }
17         }        
18     } 
19 }

         No.2 Bellman-Ford & SPFA(shortest path fast algorithm)

                   bellman-ford算法核心:

                                          扫描所有的边(x,y,z) 若dis[y] > dis[x] + z 则更新 dis[y]=dis[x]+z;

                  spfa:对bellman算法进行队列优化

                               建立一个队列 q ,起点含1,然后每次取出队头 x  ,对x的每条出边进行 dis 更新 与 vis更新 (是否入过队):如果未入过队,将其入队与更新 vis

                    时间复杂度:O(km) 或 O(nm)

code            

void spfa(){
    memset(d,0x3f,sizeof(d));
    memset(v,0,sizeof(v));
    d[1]=0,v[1]=1;
    q.push(1);
    while(q.size()){
        int x=q.front();
        q.pop();
        v[x]=0;
        for(int i=head[x];i;i=next[i]){
            int y=ver[i],z=edge[i];
            if(d[y]>d[x]+z){
                d[y]=d[x]+z;
                if(!v[y]) q.push(y),v[y]=1;
            }
        }
    }
}

 

•多源最短路径                  

         Floyd算法

                      算法核心:动态规划(dp)

                                    (1)设 f[i , j , k] 为经过不超过k的节点从i->j的最短路径长度,得状态转移方程 f[i,j,k]=min(f[i,j,k-1],f[i,k,k-1]+f[k,j,k-1]);

                                    (2) 由此开三层for循环进行更新 f ,时间复杂度为O(n3)  空间复杂度 也为 O(n3),但进行观察可以得知 f 的值 只与 k  、k-1 有关,所以可将第三维变成动态结构或者根本就不需要第三维 k ;

                                    (3) 理解f[i,j] 无k :  三层循环中 第一层必为 k ,因为考虑到dp的无后效性 ,将k看做中间节点,每次f[i,j]可与f[i,k]+f[k,j]比较:f[i,j]=min(f[i,j],f[i,k]+f[k,j])(状态转移方程)

核心code

1 for(int k=1;k<=n;k++)
2    for(int i=1;i<=n;i++)
3       for(int j=1;j<=n;j++)
4          f[i][j]=min(f[i][j],f[i][k]+f[k][j]);

 

 

                  应用:关系的传递性,可以存图时定义单向关系为1,否则为0,然后利用floyd算法进行关系的传递;

 

f[i][j] |= f[i][k] & f[k][j];

例题e.g.

No.1 单源最短路问题(模板题)

题目描述

输入一个无向网络,输出其中2个顶点之间的最短路径长度

输入

输入文件第一行为n和m,表示有n个顶点和m条带权边,其中顶点编号是从1到n,接下来有m行,每行三个整数,分别表示两个顶点编号和对应边的权值,再接下来有一行,两个整数表示要求的最短路径的两个顶点编号

输出

输出文件就一行,即两个顶点间的最短路径长度(权值和)

输入样例

4 5
1 2 2
1 3 1
2 3 2
2 4 1
3 4 6
1 4

输出样例

3

code

 1 #include<iostream>
 2 #include<cmath>
 3 #include<cstring>
 4 using namespace std;
 5 int e[1001][1001],dis[1001],vis[1001];
 6 int main() {
 7     int n,m;
 8     cin>>n>>m;
 9     memset(e,127,sizeof(e));
10     const int q=e[0][0];
11     for(int i=1; i<=m; i++) {
12         int x,y,v;
13         cin>>x>>y>>v;
14         e[x][y]=e[y][x]=v;
15     }
16     int x,y;
17     cin>>x>>y;
18     for(int i=1; i<=n; i++)
19         e[i][i]=0,dis[i]=e[x][i];
20     int ji=0;
21     while(ji<=n) {
22         int minn=q,k;
23         for(int i=1; i<=n; i++) {
24             if(!vis[i]&&dis[i]<minn) {
25                 minn=dis[i];
26                 k=i;
27             }
28         }
29         vis[k]=1;
30         ji++;
31         for(int i=1; i<=n; i++) {
32             if(e[k][i]!=q&&i!=k) {
33                 dis[i]=min(dis[i],e[k][i]+dis[k]);
34             }
35         }
36     }
37 //    for(int i=1;i<=n;i++) cout<<dis[i]<<" ";
38     cout<<dis[y];
39     return 0;
40 }/*6 9
41 1 2 1
42 1 3 12
43 2 3 9
44 2 4 3
45 3 5 5
46 4 3 4
47 4 5 13
48 4 6 15
49 5 6 4*

 No.2 最优贸易

描述

C国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市。任意两个城市之间最多只有一条道路直接相连。这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为1条。
C国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。
商人阿龙来到C国旅游。当他得知“同一种商品在不同城市的价格可能会不同”这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚一点旅费。设C国 n 个城市的标号从 1~n,阿龙决定从1号城市出发,并最终在 n 号城市结束自己的旅行。在旅游的过程中,任何城市可以被重复经过多次,但不要求经过所有 n 个城市。
阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品——水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。因为阿龙主要是来C国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。
现在给出 n 个城市的水晶球价格,m 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。请你告诉阿龙,他最多能赚取多少旅费。

输入格式

   第一行包含 2 个正整数n 和m,中间用一个空格隔开,分别表示城市的数目和道路的
数目。
   第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这n 个城
市的商品价格。
   接下来 m 行,每行有3 个正整数,x,y,z,每两个整数之间用一个空格隔开。如果z=1,表示这条道路是城市x 到城市y 之间的单向道路;如果z=2,表示这条道路为城市x 和城市y 之间的双向道路。

输出格式

一个整数,表示答案。

样例输入

5 5
4 3 5 6 1
1 2 1
1 4 1
2 3 2
3 5 1
4 5 2

样例输出

5

数据范围与约定

  • 输入数据保证 1 号城市可以到达n 号城市。
    对于 10%的数据,1≤n≤6。
    对于 30%的数据,1≤n≤100。
    对于 50%的数据,不存在一条旅游路线,可以从一个城市出发,再回到这个城市。
    对于 100%的数据,1≤n≤100000,1≤m≤500000,1≤x,y≤n,1≤z≤2,1≤各城市
    水晶球价格≤100。

来源

CCF NOIP2009

思路

           将最短路中 用 d[x] + z更新d[y] 变成 min(d[x],price[y]) 更新 d[y]

            用d[x] 表示从 1->x 的所有路径中,能够经过的权值最的权值。

            这时候不难发现,我们还应该求出从x->n 的最权值 ,这时候我们就应该存一个反图,用 f 数组来保存从x->n 的最大权值

code

 1 #include <iostream>
 2 #include <vector>
 3 #include <queue>
 4 #include <cstring>
 5 
 6 using namespace std;
 7 
 8 int a[100001],tb[100001],ta[100001];
 9 
10 vector<int> g[100001],gf[100001];
11 
12 int main()
13 {
14     int n,m;
15     cin>>n>>m;
16     for(int i=1;i<=n;++i)
17     {
18         cin>>a[i];
19     }
20     for(int i=1;i<=m;i++)
21     {
22         int x,y,z;
23         cin>>x>>y>>z;
24         g[x].push_back(y);
25         gf[y].push_back(x);
26         if(z==2)
27         {
28             g[y].push_back(x);
29             gf[x].push_back(y);
30         }
31     }
32     queue<int> q;
33     q.push(1);
34     memset(ta,127,sizeof(ta));
35     ta[1]=2147483647;
36     while(!q.empty())
37     {
38         int t=q.front();
39         q.pop();
40         ta[t]=min(ta[t],a[t]);
41         int l=g[t].size();
42         for(int i=0;i<l;i++)
43         {
44             if(ta[t]<ta[g[t][i]])
45             {
46                 ta[g[t][i]]=ta[t];
47                 q.push(g[t][i]);
48             }
49         }
50     }
51     q.push(n);
52     while(!q.empty())
53     {
54         int t=q.front();
55         q.pop();
56         tb[t]=max(tb[t],a[t]);
57         int l=gf[t].size();
58         for(int i=0;i<l;i++)
59         {
60             if(tb[t]>tb[gf[t][i]])
61             {
62                 tb[gf[t][i]]=tb[t];
63                 q.push(gf[t][i]);
64             }
65         }
66     }
67     int ans=0;
68     for(int i=1;i<=n;i++)
69     {
70         ans=max(ans,tb[i]-ta[i]);
71     }
72     cout<<ans;
73     return 0;
74 }

 

No.3 Sorting It All Out

题目点标题               思路:

                           这是一道利用于floyd的有向闭包传递问题,存图时用 1表示 a>b ,0表示<;

                           floyd传递完成后,若有d[i,j]=d[j,i]=1,则矛盾。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 bool a[30][30];
 7 int b[30],n,m,i,j,k;
 8 char x,y;
 9 int main()
10 {
11     while(~scanf("%d%d\n",&n,&m)&&n)
12     {
13         memset(a,0,sizeof(a));
14         for(i=1;i<=n;i++) a[i][i]=1;
15         for(i=1;i<=m;i++)
16         {
17             scanf("%c<%c\n",&x,&y);
18             x=x-'A'+1,y=y-'A'+1;
19             a[x][y]=1;
20             for(j=1;j<=n;j++)
21                 for(k=1;k<=n;k++)
22                     if(a[j][x]&&a[y][k]) a[j][k]=1;
23             for(j=1;j<n;j++)
24                 for(k=j+1;k<=n;k++)
25                     if(a[j][k]&&a[k][j])
26                     {
27                         printf("Inconsistency found after %d relations.\n",i);
28                         j=n; break;
29                     }
30                     else if(!a[j][k]&&!a[k][j]) x=0;
31             if(j==n+1) break;
32             if(x&&j==n)
33             {
34                 printf("Sorted sequence determined after %d relations: ",i);
35                 memset(b,0,sizeof(b));
36                 for(j=1;j<n;j++)
37                     for(k=j+1;k<=n;k++)
38                         if(a[j][k]) b[j]++; else b[k]++;
39                 for(j=n-1;j>=0;j--)
40                     for(k=1;k<=n;k++)
41                         if(b[k]==j) {putchar(k+'A'-1); break;}
42                 puts(".");
43                 break;
44             }
45         }
46         if(i>m) puts("Sorted sequence cannot be determined.");
47         for(i++;i<=m;i++) scanf("%c<%c\n",&x,&y);
48     }
49     return 0;
50 }

 

No.4 贫富差距

题目描述

一个国家有N个公民,标记为0,1,2,...,N-1,每个公民有一个存款额。已知每个公民有一些朋友,同时国家有一条规定朋友间的存款额之差不能大于d。也就是说,a和b是朋友的话,a有x元的存款,b有y元,那么|x-y|<=d。给定d值与N个人的朋友关系,求这个国家最富有的人和最贫穷的人的存款相差最大的可能值是多少?即求贫富差距的最大值的下界。若这个值为无穷大,输出-1.
输入

多组测试数据,第一行一个整数T,表示测试数据数量,1<=T<=5
每组测试数据有相同的结构构成。
每组数据的第一行两个整数N,d,表示人数与朋友间存款差的最大值,其中2<=N<=50,0<=d<=1000.
接下来有一个N*N的数组A,若A[i][j]='Y'表示i与j两个人是朋友,否则A[i][j]='N'表示不是朋友。其中A[i][i]='N',且保证
A[i][j]=A[j][i].
输出

每组数据一行输出,即这个国家的贫富差距最大值的下界,如果这个值为无穷大输出-1.
输入样例

    3
    3 10
    NYN
    YNY
    NYN
    2 1
    NN
    NN
    6 1000
    NNYNNN
    NNYNNN
    YYNYNN
    NNYNYY
    NNNYNN
    NNNYNN

输出样例

    20
    -1
    3000


思路

      floyd求多源最短路,然后将最小值拿出来*d即为answer;

code

 1 #include <stdio.h>
 2 #include <string.h>
 3 const int inf = 99999999;
 4 int map[55][55], n;
 5 void Floyd()
 6 {
 7     for (int i = 0; i < n; i++)
 8         for (int j = 0; j < n; j++)
 9             for (int k = 0; k < n; k++)
10                 if (map[j][k] > map[j][i] + map[i][k])
11                     map[j][k] = map[j][i] + map[i][k];
12 }
13 int main()
14 {
15     int d, t, ans;
16     char str[55][55];
17     scanf("%d", &t);
18     while (t--)
19     {
20         ans = -inf;
21         scanf("%d%d", &n, &d);
22         for (int i = 0; i < n; i++)
23             scanf("%s", str[i]);
24         for (int i = 0; i < n; i++)
25         {
26             for (int j = 0; j < n; j++)
27             {
28                 if (str[i][j] == 'Y')
29                     map[i][j] = 1;
30                 else
31                 {
32                     if (i != j)
33                         map[i][j] = inf;
34                     else map[i][j] = 0;
35                 }
36             }
37         }
38         Floyd();
39         for (int i = 0; i < n; i++)
40             for (int j = 0; j < n; j++)
41                 if (ans < map[i][j])
42                     ans = map[i][j];
43         if (ans < inf)
44             printf("%d\n", ans * d);
45         else printf("-1\n");
46     }
47     return 0;
48 }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!