[图论]最短路系列

Posted AntiQuality

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[图论]最短路系列相关的知识,希望对你有一定的参考价值。

昨天今天两天复习了一下最短路,

1 Floyd
2 Dijkstra
3 Bellman-ford
4 (SPFA)

(但是还有一些其他的比如:第k短路,Johnson,差分约束没有仔细研究过)


 

Dijkstra堆优化

luogu3371

题目描述

如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

输入输出格式

输入格式:

第一行包含三个整数N、M、S,分别表示点的个数、有向边的个数、出发点的编号。

接下来M行每行包含三个整数Fi、Gi、Wi,分别表示第i条有向边的出发点、目标点和长度。

输出格式:

一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i的最短路径长度(若S=i则最短路径长度为0,若从点S无法到达点i,则最短路径长度为2147483647)

说明

时空限制:1000ms,128M

对于100%的数据:N<=10000,M<=500000

#include<bits/stdc++.h>
using namespace std;
const int INF = 2147483647;
int n,m,s,d[10035];
struct NODE 
{
    int y,val;
    NODE(int a, int b):y(a), val(b) {}
};
vector<NODE> mp[10035];
struct cmp
{
    bool operator() (int &a, int &b) const 
    {
        return d[a] > d[b];
    }
};
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    priority_queue<int, vector<int>, cmp> q;
    for (int i=1; i<=n; i++)d[i] = INF;
    d[s] = 0;
    for (int i=1; i<=m; i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        mp[x].push_back(NODE(y, z));
    }
    q.push(s);
    while (!q.empty())
    {
        int tt = q.top();q.pop();
        for (int i=0; i<mp[tt].size(); i++)
        {
            if (d[mp[tt][i].y] > d[tt] + mp[tt][i].val)
            {
                d[mp[tt][i].y] = d[tt] + mp[tt][i].val;
                q.push(mp[tt][i].y);
            }
        }
    }
    for (int i=1; i<=n; i++)printf("%d ",d[i]);
    return 0;
}

luogu2384

题目描述

给定n个点的带权有向图,求从1到n的路径中边权之积最小的简单路径。

输入输出格式

输入格式:

第一行读入两个整数n,m,表示共n个点m条边。 接下来m行,每行三个正整数x,y,z,表示点x到点y有一条边权为z的边。

输出格式:

输出仅包括一行,记为所求路径的边权之积

对于100%的数据,n<=1000,m<=1000000。边权不超过10000。

#include<bits/stdc++.h>
using namespace std;
const int N = 1003;
int n,m;
long long d[N];
struct NODE
{
    int y;
    long long val;
    NODE(long long a, long long b):y(a),val(b) {}
};
struct cmp
{
    bool operator () (int &a, int &b)const 
    {
        return d[a] > d[b];
    }
};
vector<NODE>mp[N];
priority_queue<int, vector<int>, cmp> q;
int main()
{
    memset(d, 0x3f3f3f3f, sizeof(d));
    d[1] = 1;
    scanf("%d%d",&n,&m);
    for (int i=1; i<=m; i++)
    {
        int x, y;long long z;
        scanf("%d%d%lld",&x,&y,&z);
        mp[x].push_back(NODE(y, z));
    }
    q.push(1);
    while (!q.empty())
    {
        int tt = q.top();q.pop();
        for (int i=0; i<mp[tt].size(); i++)
        {
            if (d[mp[tt][i].y] > d[tt]*mp[tt][i].val)
            {
                d[mp[tt][i].y] = d[tt]*mp[tt][i].val;
                q.push(mp[tt][i].y);
            }
        }
    }
    printf("%lld\n",d[n]%9987);
    return 0;
}

普通Bellman-ford

luogu2136(求负权环)

题目描述

在小明和小红的生活中,有N个关键的节点。有M个事件,记为一个三元组(Si,Ti,Wi),表示从节点Si有一个事件可以转移到Ti,事件的效果就是使他们之间的距离减少Wi。

这些节点构成了一个网络,其中节点1和N是特殊的,节点1代表小明,节点N代表小红,其他代表进展的阶段。所有事件可以自由选择是否进行,但每次只能进行当前节点邻接的。请你帮他们写一个程序,计算出他们之间可能的最短距离。

对于全部数据,N<=1000,M<=10000,|Wi|<=100,保证从节点1到N有路径。

#include<bits/stdc++.h>
using namespace std;
const int M = 10035;
const int N = 1003;
int u[M],v[M],w[M],d[N],n,m,ans = 1e9;
bool vis[N];
inline bool BELL(int x)
{
    memset(d, 0x3f3f3f3f, sizeof(d));
    d[x] = 0;
    for (int k=1; k<n; k++)
    {
        bool fs = 1;
        for (int i=1; i<=m; i++)
            if (d[v[i]] > d[u[i]] + w[i])
            {
                d[v[i]] = d[u[i]] + w[i];
                fs = 0;
            }
        if (fs)break;
    }
    bool fl = 0;
    for (int i=1; i<=m; i++)
        if (d[v[i]] > d[u[i]] + w[i]){fl = 1; break;}
    ans = min(ans, d[n-x+1]);
    return fl;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1; i<=m; i++)scanf("%d%d%d",&u[i],&v[i],&w[i]),w[i] = -w[i];
    bool f1 = BELL(1);
    bool f2 = BELL(n);
    if (f1||f2)puts("Forever love");
    else printf("%d\n",ans);
    return 0;
}

bfs-SPFA(负权环)

luogu2850

题目描述

While exploring his many farms, Farmer John has discovered a number of amazing wormholes. A wormhole is very peculiar because it is a one-way path that delivers you to its destination at a time that is BEFORE you entered the wormhole! Each of FJ‘s farms comprises N (1 ≤ N ≤ 500) fields conveniently numbered 1..N, M (1 ≤ M ≤ 2500) paths, and W (1 ≤ W ≤ 200) wormholes.

As FJ is an avid time-traveling fan, he wants to do the following: start at some field, travel through some paths and wormholes, and return to the starting field a time before his initial departure. Perhaps he will be able to meet himself :) .

To help FJ find out whether this is possible or not, he will supply you with complete maps to F (1 ≤ F ≤ 5) of his farms. No paths will take longer than 10,000 seconds to travel and no wormhole can bring FJ back in time by more than 10,000 seconds.

John在他的农场中闲逛时发现了许多虫洞。虫洞可以看作一条十分奇特的有向边,并可以使你返回到过去的一个时刻(相对你进入虫洞之前)。John的每个农场有M条小路(无向边)连接着N (从1..N标号)块地,并有W个虫洞。其中1<=N<=500,1<=M<=2500,1<=W<=200。 现在John想借助这些虫洞来回到过去(出发时刻之前),请你告诉他能办到吗。 John将向你提供F(1<=F<=5)个农场的地图。没有小路会耗费你超过10000秒的时间,当然也没有虫洞回帮你回到超过10000秒以前。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 503;
 4 const int M = 6003;
 5 struct NODE
 6 {
 7     int y,val;
 8     NODE(int a, int b):y(a),val(b) {}
 9 };
10 int n,m,tt,w,d[N],cnt[N];
11 bool vis[N],iq[N];
12 vector<NODE> mp[N];
13 deque<int> q;
14 inline int left()
15 {
16     for (int i=1; i<=n; i++)if (!vis[i])return i;
17     return 0;
18 }
19 inline void clear(deque<int> &q)
20 {
21     deque<int> emt;
22     swap(q, emt);
23     return;
24 }
25 int main()
26 {
27     scanf("%d",&tt);
28     while (tt--)
29     {
30         scanf("%d%d%d",&n,&m,&w);
31         memset(vis, 0, sizeof(vis));
32         memset(d, 0, sizeof(d));
33         memset(mp, 0, sizeof(mp));
34         for (int i=1; i<=m; i++)
35         {
36             int x,y,z;
37             scanf("%d%d%d",&x,&y,&z);
38             mp[x].push_back(NODE(y, z));
39             mp[y].push_back(NODE(x, z));
40         }
41         for (int i=1; i<=w; i++)
42         {
43             int x,y,z;
44             scanf("%d%d%d",&x,&y,&z);
45             mp[x].push_back(NODE(y, -z));
46         }
47         int s;
48         bool fk = 0;
49         while (s = left())
50         {
51             clear(q);//printf("#%d:\n",s);
52             memset(iq, 0, sizeof(iq));
53             memset(cnt, 0, sizeof(cnt));
54             memset(d, 0x3f3f3f3f, sizeof(d));
55             q.push_back(s);
56             iq[s] = 1;vis[s] = 1;fk = 0;
57             d[s] = 0;
58             while (!q.empty())
59             {
60                 int tt = q.front();q.pop_front();
61                 iq[tt] = 0;cnt[tt]++;//printf("@@%dout\n",tt);
62                 if (cnt[tt] == n+1){fk = 1;break;}
63                 for (int i=0; i<mp[tt].size(); i++)
64                 {
65                     vis[mp[tt][i].y] = 1;
66                     if (d[mp[tt][i].y] > d[tt] + mp[tt][i].val)
67                     {
68                         d[mp[tt][i].y] = d[tt] + mp[tt][i].val;
69                         if (!iq[mp[tt][i].y])
70                         {
71                             iq[mp[tt][i].y] = 1;//printf("@%dpush\n",mp[tt][i].y);
72                             if (q.empty())q.push_back(mp[tt][i].y);
73                             else{
74                                 if (d[q.front()] > d[mp[tt][i].y])
75                                     q.push_front(mp[tt][i].y);
76                                 else q.push_back(mp[tt][i].y);
77                             }
78                         }
79                     }
80                 }
81             }
82             if (fk)break;
83         }
84         if (fk)puts("YES");
85         else puts("NO");
86     }
87     return 0;
88 }

或者,bfs-SPFA有一种小优化。将dis数组初始化为0,再将vis数组的更新放进dis[]与mp[]的松弛里。

技术分享图片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 503;
 4 const int M = 6003;
 5 struct NODE
 6 {
 7     int y,val;
 8     NODE(int a, int b):y(a),val(b) {}
 9 };
10 int n,m,tt,w,d[N],cnt[N];
11 bool vis[N],iq[N];
12 vector<NODE> mp[N];
13 deque<int> q;
14 inline int left()
15 {
16     for (int i=1; i<=n; i++)if (!vis[i])return i;
17     return 0;
18 }
19 inline void clear(deque<int> &q)
20 {
21     deque<int> emt;
22     swap(q, emt);
23     return;
24 }
25 int main()
26 {
27     scanf("%d",&tt);
28     while (tt--)
29     {
30         scanf("%d%d%d",&n,&m,&w);
31         memset(vis, 0, sizeof(vis));
32         memset(d, 0, sizeof(d));
33         memset(mp, 0, sizeof(mp));
34         for (int i=1; i<=m; i++)
35         {
36             int x,y,z;
37             scanf("%d%d%d",&x,&y,&z);
38             mp[x].push_back(NODE(y, z));
39             mp[y].push_back(NODE(x, z));
40         }
41         for (int i=1; i<=w; i++)
42         {
43             int x,y,z;
44             scanf("%d%d%d",&x,&y,&z);
45             mp[x].push_back(NODE(y, -z));
46         }
47         int s;
48         bool fk = 0;
49         while (s = left())
50         {
51             clear(q);
52             memset(iq, 0, sizeof(iq));
53             memset(cnt, 0, sizeof(cnt));
54             memset(d, 0, sizeof(d));
55             q.push_back(s);
56             iq[s] = 1;vis[s] = 1;fk = 0;
57             d[s] = 0;
58             while (!q.empty())
59             {
60                 int tt = q.front();q.pop_front();
61                 iq[tt] = 0;cnt[tt]++;
62                 if (cnt[tt] == n+1){fk = 1;break;}
63                 for (int i=0; i<mp[tt].size(); i++)
64                 {
65                     if (d[mp[tt][i].y] > d[tt] + mp[tt][i].val)
66                     {
67                         d[mp[tt][i].y] = d[tt] + mp[tt][i].val;vis[mp[tt][i].y] = 1;
68                         if (!iq[mp[tt][i].y])
69                         {
70                             iq[mp[tt][i].y] = 1;
71                             if (q.empty())q.push_back(mp[tt][i].y);
72                             else{
73                                 if (d[q.front()] > d[mp[tt][i].y])
74                                     q.push_front(mp[tt][i].y);
75                                 else q.push_back(mp[tt][i].y);
76                             }
77                         }
78                     }
79                 }
80             }
81             if (fk)break;
82         }
83         if (fk)puts("YES");
84         else puts("NO");
85     }
86     return 0;
87 }
bfs-SPFA2

dfs-SPFA(负权环)

luogu3385

输入输出格式

输入格式:

第一行一个正整数T表示数据组数,对于每组数据:

第一行两个正整数N M,表示图有N个顶点,M条边

接下来M行,每行三个整数a b w,表示a->b有一条权值为w的边(若w<0则为单向,否则双向)

输出格式:

共T行。对于每组数据,存在负环则输出一行"YE5"(不含引号),否则输出一行"N0"(不含引号)。

说明

N,M,|w|≤200 000;1≤a,b≤N;T≤10 此题普通Bellman-Ford或BFS-SPFA会TLE

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 struct NODE
 4 {
 5     int y,val;
 6     NODE(int a, int b):y(a),val(b) {}
 7 };
 8 const int N = 200355;
 9 const int M = 200355;
10 vector<NODE>mp[N];
11 int n,m,tt,d[N];
12 bool vis[N],done;
13 inline void init()
14 {
15     memset(vis, 0, sizeof(vis));
16     memset(d, 0, sizeof(d));
17     for (int i=1; i<=n; i++)mp[i].clear();
18     done = 0;
19     scanf("%d%d",&n,&m);
20     for (int i=1; i<=m; i++)
21     {
22         int x,y,z;
23         scanf("%d%d%d",&x,&y,&z);
24         mp[x].push_back(NODE(y, z));
25         if (z>=0) mp[y].push_back(NODE(x, z));
26     }
27     return;
28 }
29 inline void SPFA(int x)
30 {
31     vis[x] = 1;
32     for (int i=0; i<mp[x].size(); i++)
33     {
34         int u = x, v = mp[x][i].y, w = mp[x][i].val;
35         if (d[u] + w < d[v]){
36             if (vis[v]){done = 1;return;}
37             d[v] = d[u] + w;
38             SPFA(v);
39         }
40     }
41     vis[x] = 0;
42     return;
43 }
44 int main()
45 {
46     scanf("%d",&tt);
47     while (tt--)
48     {
49          init();
50          for (int i=1; i<=n; i++)
51          {
52              if (!vis[i])SPFA(i);
53              if (done)break;
54          }
55          if (done)puts("YE5");
56          else puts("N0");
57     }
58     return 0;
59 }

这一题我的bfs-SPFA就是卡不过去……学习了一发dfs-SPFA。

理解也不是很难。由于bfs主要用于扩展最短路,而dfs可以更快地找到负环。

 


SPFA(最短路)

luogu2951

题目描述

Bessie is playing hide and seek (a game in which a number of players hide and a single player (the seeker) attempts to find them after which various penalties and rewards are assessed; much fun usually ensues).

She is trying to figure out in which of N (2 <= N <= 20,000) barns conveniently numbered 1..N she should hide. She knows that FJ (the seeker) starts out in barn 1. All the barns are connected by M (1 <= M <= 50,000) bidirectional paths with endpoints A_i and B_i (1 <= A_i <= N; 1 <= B_i <= N; A_i != B_i); it is possible to reach any barn from any other through the paths.

Bessie decides that it will be safest to hide in the barn that has the greatest distance from barn 1 (the distance between two barns is the smallest number of paths that one must traverse to get from one to the other). Help Bessie figure out the best barn in which to hide.

奶牛贝西和农夫约翰(FJ)玩捉迷藏,现在有N个谷仓,FJ开始在第一个谷仓,贝西为了不让FJ找到她,当然要藏在距离第一个谷仓最远的那个谷仓了。现在告诉你N个谷仓,和M个两两谷仓间的“无向边”。每两个仓谷间当然会有最短路径,现在要求距离第一个谷仓(FJ那里)最远的谷仓是哪个(所谓最远就是距离第一个谷仓最大的最短路径)?如有多个则输出编号最小的。以及求这最远距离是多少,和有几个这样的谷仓距离第一个谷仓那么远。

输入输出格式

输入格式:

* Line 1: Two space-separated integers: N and M

* Lines 2..M+1: Line i+1 contains the endpoints for path i: A_i and B_i

第一行:两个整数N,M;

第2-M+1行:每行两个整数,表示端点A_i 和 B_i 间有一条无向边。

输出格式:

* Line 1: On a single line, print three space-separated integers: the index of the barn farthest from barn 1 (if there are multiple such barns, print the smallest such index), the smallest number of paths needed to reach this barn from barn 1, and the number of barns with this number of paths.

仅一行,三个整数,两两中间空格隔开。表示:距离第一个谷仓最远的谷仓编号(如有多个则输出编号最小的。),以及最远的距离,和有几个谷仓距离第一个谷仓那么远。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 20035;
 4 const int M = 50035;
 5 int n,m,d[N];
 6 bool vis[N];
 7 vector<int>mp[N];
 8 queue<int>q;
 9 int main()
10 {
11     scanf("%d%d",&n,&m);
12     for (int i=1; i<=m; i++)
13     {
14         int x,y;
15         scanf("%d%d",&x,&y);
16         mp[x].push_back(y);
17         mp[y].push_back(x);
18     }
19     memset(d, 0x3f3f3f3f, sizeof(d));
20     d[1] = 0;
21     q.push(1);
22     while (q.size())
23     {
24         int tt = q.front();q.pop();
25         vis[tt] = 0;
26         for (int i=0; i<mp[tt].size(); i++)
27             if (d[tt] + 1 < d[mp[tt][i]])
28             {
29                 d[mp[tt][i]] = d[tt] + 1;
30                 if (!vis[mp[tt][i]]){
31                     q.push(mp[tt][i]);
32                     vis[tt] = 1;
33                 }
34             }
35     }
36     int lb, mn = *max_element(d+1, d+n+1), cnt = 0;
37     for (int i=n; i>=1; i--)
38         if (d[i] == mn){cnt++;lb = i;}
39     printf("%d %d %d",lb,mn,cnt);
40     return 0;
41 }

 

以上是关于[图论]最短路系列的主要内容,如果未能解决你的问题,请参考以下文章

图论----最短路问题

图论:图的四种最短路径算法

图论:图的四种最短路径算法

图论训练之三

图论 最短路总结

图论算法之最短路径