图论常见算法

图论

并查集

卡码网题目链接(ACM模式)

#include <bits/stdc++.h>

using namespace std;


int find(int x, vector<int>& root) {
    int t = x;
    while (root[t] != t) {
        t = root[t];
    }
    int ans = t;
    t = x;
    while (root[t] != t) {
        int tmp = root[t];
        root[t] = ans;
        t = tmp;
    }
    return ans;
}

bool joined(int x, int y, vector<int>& root) {
    if (x == y) return true;
    int x_root = find(x, root);
    int y_root = find(y, root);
    return x_root == y_root;
}

void join(int x, int y, vector<int>& root) {
    if (x == y) return ;
    int x_root = find(x, root);
    int y_root = find(y, root);
    root[x_root] = y_root;
}

int main() {
    int n;
    cin >> n;
    std::vector<int> root(n + 1);
    for (int i = 1; i <= n; ++i) {
        root[i] = i;
    }
    for (int i = 1; i <= n ;++i) {
        int s, e;
        cin >> s >> e;
        if (joined(s, e, root)) {
            cout << s << " " << e << endl;
            return 0;
        } else {
            join(s, e, root);
        }
    }
}

拓扑排序

卡码网:117. 软件构建

#include <bits/stdc++.h>

using namespace std;

vector<int> topo() {
    int M, N, s, e;
    cin >> N >> M;
    vector<vector<int>> G(N, vector<int>(N, 0));    // 邻接矩阵
    vector<int> path, in(N, 0);                     // 入度
    queue<int> q;
    while (M--) {
        cin >> s >> e;
        G[s][e] = 1;
        ++in[e];    // 入度+1
    }
    for (int i = 0; i < N; ++i) {                   // 找出入度为0的节点,入队
        if (in[i] == 0) {
            q.push(i);
        }
    }
    while (!q.empty()) {
        int v = q.front(); q.pop();
        path.push_back(v);
        for (int i = 0; i < N; ++i) {               // 减少与v相邻的节点的入度
            if (G[v][i]) {
                if (--in[i] == 0) {
                    q.push(i);
                }
            }
        }
    }
    if (path.size() == N) return path;
    else return {};
}

int main() {
    auto path = topo();
    if (path.size()) {
        for (int i = 0; i < path.size() - 1; ++i) {
            cout << path[i] << " ";
        }
        cout << path.back() << endl;
    } else {
        cout << -1 << endl;
    }
}

最小生成树prim

卡码网:53. 寻宝

#include <bits/stdc++.h>

using namespace std;

pair<bool, int> prim(const int V, const vector<vector<int>>& G, int start = 1) {
    vector<int> joined(V + 1, 0);           // 标记节点是否加入当前最小生成树
    vector<int> dist(V + 1, INT_MAX);       // 节点与当前最小生成树任意节点的最小距离
    dist[start] = 0;
    int totalCost = 0;      // 总的cost
    int joinedNodes = 0;    // 已加入最小生成树的节点个数
    for (int i = 1; i <= V; ++i) {   // 依次选取 V 个节点加入到最小生成树
        int v = -1, minDist = INT_MAX;
        for (int i = 1; i <= V; ++i) {
            if (dist[i] < minDist && joined[i] == 0) {
                v = i;
                minDist = dist[i];
            }
        }
        if (v == -1) {
            break;
        }
        ++joinedNodes;
        totalCost += minDist;
        joined[v] = 1;
        for (int j = 1; j <= V; ++j) {
            if (joined[j] == 0 /*j还没有加入到最小生成树*/ && G[v][j] < dist[j] /*可收缩距离*/) {
                dist[j] = G[v][j];
            }
        }
    }
    return {joinedNodes == V, totalCost};
}

int main() {
    int V, E;   // V : 节点数,E :边数
    cin >> V >> E;
    vector<vector<int>> G(V + 1, vector<int>(V + 1, INT_MAX));
    while (E--) {
        int s, e, cost;
        cin >> s >> e >> cost;
        if (G[s][e] > cost) {   // 选最短的边
            G[s][e] = G[e][s] = cost;
        }
    }
    auto [f, totalCost] = prim(V, G);
    if (f) cout << totalCost << endl;
}

最小生成树kruskal

卡码网:53. 寻宝

#include <bits/stdc++.h>

using namespace std;

struct Edge {
    int start, end, val;
    bool operator < (const Edge& rhs) { return val < rhs.val; }
};

class UnionSet {
public:
    UnionSet(size_t v) : V(v), father(V + 1) {
        for (int i = 1; i <= V; ++i) {
            father[i] = i;
        }
    }

    bool isSame(int u, int v) {
        return u == v || find(u) == find(v);
    }

    int find(int u) {
        return father[u] == u ? u : father[u] = find(father[u]);
    }

    bool join(int u, int v) {
        int uf = find(u);
        int vf = find(v);
        if (uf == vf) return false;
        father[uf] = vf;
        return true;
    }

    bool isOnlyOne() {
        bool f = true;
        for (int i = 1; i <= V; ++i) {
            if (!isSame(i, 1)) {
                f = false;
                break;
            }
        }
        return f;
    }
private:
    size_t V;
    vector<int> father; 
};

int kruskal() {
    int V, E;       // V : 节点数,E :边数
    int s, e, v;    // s : 起点, e :终点, v : 权值
    cin >> V >> E;
    int totalCost = 0;
    vector<Edge> edges;
    edges.reserve(E);
    UnionSet unionSet(V);
    while (E--) {
        cin >> s >> e >> v;
        edges.push_back(Edge{.start = s, .end = e, .val = v});
    }
    sort(edges.begin(), edges.end());   // 按权值对边排序
    for (const Edge& edge : edges) {
        if (unionSet.join(edge.start, edge.end)) {  // 合并
            totalCost += edge.val;
        }
    }
    if (unionSet.isOnlyOne()) return totalCost;
    else return -1;
}

int main() {
    int totalCost = kruskal();
    cout << totalCost << endl;
}

单源最短路径dijkstra

卡码网:47. 参加科学大会

#include <bits/stdc++.h>

using namespace std;

int dijkstra(int start = 1) {
    int N, M, S, E, V;   // N : 节点数,M :边数, S : 边起点, E : 边终点, V :边权值
    cin >> N >> M;
    vector<vector<int>> G(V + 1, vector<int>(N + 1, INT_MAX));
    vector<int> visited(N + 1, 0);          // 标记节点是否已经算出与起点start的最短距离
    vector<int> dist(N + 1, INT_MAX);       // 节点与起点start的最短距离
    dist[start] = 0;
    while (M--) {
        cin >> S >> E >> V;
        if (G[S][E] > V) {   // 选最短的边
            G[S][E] = V;     // 有向图
        }
    }
    for (int i = 1; i <= N; ++i) {   // 依次选取 V 个节点加入到最小生成树
        int v = -1, minDist = INT_MAX;
        for (int i = 1; i <= N; ++i) {
            if (dist[i] < minDist && visited[i] == 0) {
                v = i;
                minDist = dist[i];
            }
        }
        if (v == -1) {
            break;
        }
        visited[v] = 1;
        for (int j = 1; j <= N; ++j) {
            if (G[v][j] != INT_MAX /*v、j之间有边*/  && visited[j] == 0 /*j还没有加入到最小生成树*/ && dist[v] + G[v][j] < dist[j] /*可收缩距离*/) {
                dist[j] = dist[v] + G[v][j];
            }
        }
    }
    return dist[N] == INT_MAX ? -1 : dist[N];
}

int main() {
    cout << dijkstra() << endl;
}

多源最短路径floyd

卡码网:97. 小明逛公园

#include <bits/stdc++.h>

using namespace std;

vector<int> floyd() {
    int N, M, u, v, w, numOfPlans;
    cin >> N >> M;
    vector<vector<int>> G(N + 1, vector<int>(N + 1, INT_MAX));
    vector<int> ans;
    vector<pair<int, int>> plans;
    while (M--) {
        cin >> u >> v >> w;
        if (G[u][v] > w) {
            G[u][v] = G[v][u] = w;
        }
    }
    cin >> numOfPlans;
    while (numOfPlans--) {
        cin >> u >> v;
        plans.emplace_back(u, v);
    }
    for (int k = 1; k <= N; ++k) { // 选择不同的中转节点
        for (int i = 1; i <= N; ++i) {
            for (int j = 1; j <= N; ++j) {
                if (G[i][k] != INT_MAX && G[k][j] != INT_MAX && G[i][k] + G[k][j] < G[i][j]) {
                    G[i][j] = G[i][k] + G[k][j];
                }
            }
        }
    }
    for (const auto& plan : plans) {
        ans.push_back(G[plan.first][plan.second] == INT_MAX ? -1 : G[plan.first][plan.second]);
    }
    return ans;
}

int main() {
    auto dist = floyd();
    for (const int d : dist) {
        cout << d << endl;
    }
    return 0;
}

单源最短路径SPFA(负环)

卡码网:95. 城市间货物运输 II

#include <bits/stdc++.h>

using namespace std;

struct Edge {
    int s, t, v;
    Edge(int s, int t, int v) : s(s), t(t), v(v) {}
};

void SPFA() {
    int n, m, s, t, v;
    bool circle = false;
    cin >> n >> m;
    int start = 1, end = n;
    vector<list<Edge>> G(n + 1);
    vector<int> count(n + 1, 0), dist(n + 1, INT_MAX);
    queue<int> q;
    while (m--) {
        cin >> s >> t >> v;
        G[s].push_back(Edge(s, t, v));
    }
    dist[start] = 0;
    count[start]++;
    q.push(start);
    while (!q.empty()) {
        int node = q.front(); q.pop();
        for (const Edge& edge : G[node]) {
            int from = node, to = edge.t, weight = edge.v;
            if (dist[to] > dist[from] + weight) {
                dist[to] = dist[from] + weight;
                q.push(to);
                ++count[to];
                if (count[to] == n) {
                    circle = true;
                    queue<int> tmpq;
                    q.swap(tmpq);   // 清空队列,防止死循环
                    break;
                }
            }
        }
    }
    
    if (circle) {
        cout << "circle" << endl;
    } else if (dist[end] == INT_MAX) {
        cout << "unconnected" << endl;
    } else {
        cout << dist[end] << endl;
    }
}

int main() {
    SPFA();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值