[一本通学习笔记] 最短路径

最短路径常用算法有Dijkstra和SPFA。SPFA支持负数权重,但容易被毒瘤数据卡。
想让SPFA跑快点可以加入一个小优化:用deque代替queue,然后在Push的时候分类,如果比当前front的dis要小就Push Front,否则Push Back。
最短路计数和次短路问题仿照普通DP处理即可。次短路中严格非严格注意区别对待。
建模方面主要考虑分层图等常见技巧。处理实际问题时可能结合二分答案或者枚举进行。

10074. 「一本通 3.2 例 3」架设电话线

#include <bits/stdc++.h>
using namespace std;
const int MAX_NODE = 100005;
const int MAX_EDGE = 1000005;
template <class T>
class Graph_SP {  // 解决单源最短路径问题
public:
    vector<pair<int, T> > G[MAX_NODE];
    int d[MAX_NODE], v[MAX_NODE], f[MAX_NODE];
    T t[MAX_NODE];                     // 距离表与访问标识表
    void make(int t1, int t2, T t3) {  // 造边(有向)
        G[t1].push_back(make_pair(t2, t3));
    }
    void reset_graph(int n) {  // 用于清除图邻接表
        for (int i = 0; i <= n; i++) G[i].clear();
    }
    void reset_solver(int n) {  // 对距离表与访问标识表的清除 如果改变了类型,该函数可能需要重写!
        memset(d, 0x3f, sizeof d);
        memset(v, 0x00, sizeof v);
        memset(f, 0x00, sizeof f);
    }
    void solveSPFA(int v0, int n) {  // 执行主计算任务(使用SPFA)
        queue<int> q;
        reset_solver(n);  // 自动调用对距离表与访问标识表的清除
        // d[0]=0;
        d[v0] = 0;
        v[v0] = 1;
        q.push(v0);
        while (q.size()) {
            int p = q.front();
            q.pop();
            v[p] = 0;
            for (int i = 0; i < G[p].size(); i++) {
                int x = G[p][i].first;  // x为当前枚举边的终点,
                T y = G[p][i].second;   // y为当前枚举边的权值
                if (d[x] > d[p] + y) {
                    d[x] = d[p] + y;
                    t[x] = y;
                    f[x] = p;
                    if (!v[x])
                        q.push(x);
                }
            }
        }
    }
};
Graph_SP<int> g;
int n, m, k;
struct Edge {
    int u, v, w;
    bool operator<(const Edge &b) { return w < b.w; }
} e[MAX_EDGE];
int check(int lim) {
    g.reset_graph(n);
    for (int i = 1; i <= m; i++) {
        g.make(e[i].u, e[i].v, (e[i].w > lim));
        g.make(e[i].v, e[i].u, (e[i].w > lim));
    }
    g.solveSPFA(1, n);
    return g.d[n];
}
int main() {
    cin >> n >> m >> k;
    for (int i = 1; i <= m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        e[i].u = u;
        e[i].v = v;
        e[i].w = w;
    }
    sort(e + 1, e + m + 1);
    int l = 0, r = 1e+9;
    while (r > l) {
        int mid = (l + r) >> 1;
        if (check(mid) > k)
            l = mid + 1;
        else
            r = mid;
    }
    cout << (l >= 1e+9 ? -1 : l) << endl;
}

10075. 「一本通 3.2 练习 1」农场派对

#include <bits/stdc++.h>
using namespace std;

const int MAX_NODE = 100005;
const int MAX_EDGE = 200005;

template <class T>
class Graph_SP {  // 解决单源最短路径问题
public:
    vector<pair<int, T> > G[MAX_NODE];
    int d[MAX_NODE], v[MAX_NODE];           // 距离表与访问标识表
    void make_edge(int t1, int t2, T t3) {  // 造边(有向)
        G[t1].push_back(make_pair(t2, t3));
    }
    void reset_graph(int n) {  // 用于清除图邻接表
        for (int i = 0; i <= n; i++) G[i].clear();
    }
    void reset_solver(int n) {  // 对距离表与访问标识表的清除 如果改变了类型,该函数可能需要重写!
        memset(d, 0x3f, sizeof d);
        memset(v, 0x00, sizeof v);
    }
    void solveSPFA(int v0, int n) {  // 执行主计算任务(使用SPFA)
        queue<int> q;
        reset_solver(n);  // 自动调用对距离表与访问标识表的清除
        d[v0] = 0;
        v[v0] = 1;
        q.push(v0);
        while (q.size()) {
            int p = q.front();
            q.pop();
            v[p] = 0;
            for (int i = 0; i < G[p].size(); i++) {
                int x = G[p][i].first;  // x为当前枚举边的终点,
                T y = G[p][i].second;   // y为当前枚举边的权值
                if (d[x] > d[p] + y) {
                    d[x] = d[p] + y;
                    if (!v[x])
                        q.push(x);
                }
            }
        }
    }
};
Graph_SP<int> g1, g2;
int n, m, x, u, v, w;

int main() {
    cin >> n >> m >> x;
    for (int i = 1; i <= m; i++) {
        cin >> u >> v >> w;
        g1.make_edge(u, v, w);
        g2.make_edge(v, u, w);
    }
    g1.solveSPFA(x, n);
    g2.solveSPFA(x, n);
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        ans = max(ans, g1.d[i] + g2.d[i]);
    }
    cout << ans << endl;
}

10076. 「一本通 3.2 练习 2」Roadblocks

#include <bits/stdc++.h>
using namespace std;
const int MAX_NODE = 5005;
const int MAX_EDGE = 200005;
template <class T>
class Graph_SP {  // 解决单源最短路径问题
public:
    vector<pair<int, T> > G[MAX_NODE];
    int d[MAX_NODE], v[MAX_NODE], s[MAX_NODE];  // 距离表与访问标识表
    void make_edge(int t1, int t2, T t3) {      // 造边(有向)
        G[t1].push_back(make_pair(t2, t3));
    }
    void reset_graph(int n) {  // 用于清除图邻接表
        for (int i = 0; i <= n; i++) G[i].clear();
    }
    void reset_solver(int n) {  // 对距离表与访问标识表的清除 如果改变了类型,该函数可能需要重写!
        memset(d, 0x3f, sizeof d);
        memset(s, 0x3f, sizeof d);
        memset(v, 0x00, sizeof v);
    }
    void solveSPFA(int v0, int n) {  // 执行主计算任务(使用SPFA)
        queue<int> q;
        reset_solver(n);  // 自动调用对距离表与访问标识表的清除
        d[v0] = 0;
        v[v0] = 1;
        q.push(v0);
        while (q.size()) {
            int p = q.front();
            q.pop();
            v[p] = 0;
            for (int i = 0; i < G[p].size(); i++) {
                int x = G[p][i].first;  // x为当前枚举边的终点,
                T y = G[p][i].second;   // y为当前枚举边的权值
                if (d[x] > d[p] + y) {
                    s[x] = d[x];
                    d[x] = d[p] + y;
                    if (!v[x])
                        q.push(x), v[x] = 1;
                }
                if (s[x] > d[p] + y && d[p] + y != d[x]) {
                    s[x] = d[p] + y;
                    if (!v[x])
                        q.push(x), v[x] = 1;
                }
                if (s[x] > s[p] + y && s[p] + y != d[x]) {
                    s[x] = s[p] + y;
                    if (!v[x])
                        q.push(x), v[x] = 1;
                }
            }
        }
    }
};
Graph_SP<int> g;
int n, m, u, v, w;

int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        cin >> u >> v >> w;
        g.make_edge(u, v, w);
        g.make_edge(v, u, w);
    }
    g.solveSPFA(1, n);
    cout << g.s[n] << endl;
}

10078. 「一本通 3.2 练习 4」新年好

#include <bits/stdc++.h>
using namespace std;
const int MAX_NODE = 100005;
const int MAX_EDGE = 1000005;
template <class T>
class Graph_SP {  // 解决单源最短路径问题
public:
    vector<pair<int, T> > G[MAX_NODE];
    int d[MAX_NODE], v[MAX_NODE];           // 距离表与访问标识表
    void make_edge(int t1, int t2, T t3) {  // 造边(有向)
        G[t1].push_back(make_pair(t2, t3));
    }
    void reset_graph(int n) {  // 用于清除图邻接表
        for (int i = 0; i <= n; i++) G[i].clear();
    }
    void reset_solver(int n) {  // 对距离表与访问标识表的清除 如果改变了类型,该函数可能需要重写!
        memset(d, 0x3f, sizeof d);
        memset(v, 0x00, sizeof v);
    }
    void solveSPFA(int v0, int n) {  // 执行主计算任务(使用SPFA)
        queue<int> q;
        reset_solver(n);  // 自动调用对距离表与访问标识表的清除
        d[v0] = 0;
        v[v0] = 1;
        q.push(v0);
        while (q.size()) {
            int p = q.front();
            q.pop();
            v[p] = 0;
            for (int i = 0; i < G[p].size(); i++) {
                int x = G[p][i].first;  // x为当前枚举边的终点,
                T y = G[p][i].second;   // y为当前枚举边的权值
                if (d[x] > d[p] + y) {
                    d[x] = d[p] + y;
                    if (!v[x])
                        q.push(x), v[x] = 1;
                }
            }
        }
    }
};

Graph_SP<int> g[6];
int n, m, p[6], u, v, w;
int perm[6] = { 0, 1, 2, 3, 4, 5 };

int main() {
    cin >> n >> m >> p[1] >> p[2] >> p[3] >> p[4] >> p[5];
    p[0] = 1;
    for (int i = 1; i <= m; i++) {
        cin >> u >> v >> w;
        for (int j = 0; j < 6; j++) g[j].make_edge(u, v, w), g[j].make_edge(v, u, w);
    }
    for (int i = 0; i < 6; i++) {
        g[i].solveSPFA(p[i], n);
    }
    int ans = 1e+9;
    while (next_permutation(perm + 1, perm + 6)) {
        int sum = 0;
        for (int i = 1; i < 6; i++) sum += g[perm[i - 1]].d[p[perm[i]]];
        ans = min(ans, sum);
    }
    cout << ans << endl;
}

10081. 「一本通 3.2 练习 7」道路和航线

#include <bits/stdc++.h>
using namespace std;

const int MAX_NODE = 100005;
const int MAX_EDGE = 1000005;
template <class T>
class Graph_SP {  // 解决单源最短路径问题
public:
    vector<pair<int, T> > G[MAX_NODE];
    int d[MAX_NODE], v[MAX_NODE];           // 距离表与访问标识表
    void make_edge(int t1, int t2, T t3) {  // 造边(有向)
        G[t1].push_back(make_pair(t2, t3));
    }
    void reset_graph(int n) {  // 用于清除图邻接表
        for (int i = 0; i <= n; i++) G[i].clear();
    }
    void reset_solver(int n) {  // 对距离表与访问标识表的清除 如果改变了类型,该函数可能需要重写!
        memset(d, 0x3f, sizeof d);
        memset(v, 0x00, sizeof v);
    }
    void solveSPFA(int v0, int n) {  // 执行主计算任务(使用SPFA)
        deque<int> q;
        reset_solver(n);  // 自动调用对距离表与访问标识表的清除
        d[v0] = 0;
        v[v0] = 1;
        q.push_front(v0);
        while (q.size()) {
            int p = q.front();
            q.pop_front();
            v[p] = 0;
            for (int i = 0; i < G[p].size(); i++) {
                int x = G[p][i].first;  // x为当前枚举边的终点,
                T y = G[p][i].second;   // y为当前枚举边的权值
                if (d[x] > d[p] + y) {
                    d[x] = d[p] + y;
                    if (!v[x]) {
                        v[x] = 1;
                        if (!q.empty() && d[x] < d[q.front()])
                            q.push_front(x);
                        else
                            q.push_back(x);
                    }
                }
            }
        }
    }
};

int n, m, p, s, u, v, w;
Graph_SP<int> g;

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> m >> p >> s;
    for (int i = 1; i <= m; i++) {
        cin >> u >> v >> w;
        g.make_edge(u, v, w);
        g.make_edge(v, u, w);
    }
    for (int i = 1; i <= p; i++) {
        cin >> u >> v >> w;
        g.make_edge(u, v, w);
    }
    g.solveSPFA(s, n);
    for (int i = 1; i <= n; i++) {
        if (g.d[i] >= 0x3f3f3f3f)
            cout << "NO PATH" << endl;
        else
            cout << g.d[i] << endl;
    }
}
原文地址:https://www.cnblogs.com/mollnn/p/11615611.html