1. 程式人生 > >一句話圖論演算法_8種

一句話圖論演算法_8種

一句話Dijkstra演算法

從源點開始更新鄰邊,遍歷到達其最近的點,以其作為新源點,重複操作直到所有點更新完畢。

#include <iostream>
#include <vector>
using namespace std;

struct Node {
    int to, val;
    Node (int t, int v) : to(t), val(v) {}
};

const int maxn = 10005;

int n, m;
vector<Node> G[maxn];
int vis[maxn] = {}, dis[maxn];

void
Dijkstra() { dis[1] = 0; for (int i = 1; i <= n; ++i) { int u = -1, _min = 0x3f3f3f3f; for (int j = 1; j <= n; ++j) { if (!vis[j] && _min > dis[j]) { _min = dis[j]; u = j; } } if (u == -1) break
; vis[u] = true; for (int j = 0; j < G[u].size(); ++j) { int v = G[u][j].to, d = G[u][j].val; dis[v] = min(dis[v], dis[u] + d); } } } int main() { cin >> n >> m; for (int i = 1; i <= n; ++i) dis[i] = 0x3f3f3f3f; for
(int i = 0; i < m; ++i) { int from, to, val; cin >> from >> to >> val; G[from].push_back(Node(to, val)); G[to].push_back(Node(from, val)); } Dijkstra(); for (int i = 1; i <= n; ++i) cout << dis[i] << ' '; }

一句話SPFA演算法

一條邊當且僅當有和他相連的邊更新後它才會更新,使用佇列不斷找出這樣的邊,直到無法找出

#include <iostream>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;

struct Node {
    int to, val;
    Node (int t, int v): to(t), val(v) {}
};

const int maxn = 10005;
const int INF = 0x3f3f3f3f;

int n, m;
vector<Node> G[maxn];
int dis[maxn];
bool inq[maxn] = {};

void SPFA()
{
    dis[1] = 0;
    queue<int> Q;
    Q.push(1);
    inq[1] = true;
    while (!Q.empty()) {
        int now = Q.front();
        Q.pop();
        inq[now] = false;
        for (int i = 0; i < G[now].size(); ++i) {
            int u = now;
            int v = G[now][i].to, d = G[now][i].val;
            if (dis[v] > dis[u] + d) {
                dis[v] = dis[u] + d;
                if (!inq[v]) {
                    Q.push(v);
                    inq[v] = true;
                }
            }
        }
    }
}

int main()
{
    memset(dis, 0x3f, sizeof(dis));
    cin >> n >> m;
    for (int i = 0; i < m; ++i) {
        int from, to, val;
        cin >> from >> to >> val;
        G[from].push_back(Node(to, val));
        G[to].push_back(Node(from, val));
    }
    SPFA();
    for (int i = 1; i <= n; ++i)
        cout << dis[i] << ' ';
}

一句話Kruskal演算法

給邊按權值從小到大排序,依次找出使邊不成環的所有邊,所構成圖即為最小生成樹。

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

struct Edge {
    int from, to, cost;
    Edge (int f, int t, int c): from(f), to(t), cost(c) {}
};

const int maxn = 10005;
const int INF = 0x3f3f3f3f;

int n, m;
vector<Edge> edge;
vector<int> ans;
int father[maxn];

bool cmp(Edge x, Edge y)
{
    return x.cost < y.cost;
}

void init()
{
    for (int i = 1; i <= n; ++i)
        father[i] = i;
}

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

void Kruskal()
{
    sort(edge.begin(), edge.end(), cmp);
    for (size_t i = 0; i < edge.size(); ++i) {
        int x = find(edge[i].from), y = find(edge[i].to);
        if (x != y) {
            father[x] = y;
            ans.push_back(edge[i].cost);
        }
    }
}

int main()
{
    cin >> n >> m;
    init();
    for (int i = 0; i < m; ++i) {
        int from, to, cost;
        cin >> from >> to >> cost;
        edge.push_back(Edge(from, to, cost));
    }
    Kruskal();
    for (int i = 0; i < ans.size(); ++i)
        cout << ans[i] << ' ';
}

一句話Prim演算法

類似Dijkstra演算法,從源點出發,更新其鄰邊所連點,找到最短距離點,加入點集作為源點集,重複步驟直到所有點新增完畢

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;

struct Node {
    int to, cost;
    Node (int t, int c) : to(t), cost(c) {}
};

const int maxn = 10005;
const int INF = 0x3f3f3f3f;

int n, m;
int dis[maxn] = {};
bool vis[maxn] = {};
vector<Node> G[maxn];
vector<int> ans;

void Prim()
{
    dis[1] = 0;
    for (int i = 1; i <= n; ++i) {
        int u = -1, _min = 0x3f3f3f3f;
        for (int j = 1; j <= n; ++j) {
            if (!vis[j] && dis[j] < _min) {
                _min = dis[j];
                u = j;
            }
        }
        if (u == -1) break;
        vis[u] = true;
        ans.push_back(_min);
        for (size_t j = 0; j < G[u].size(); ++j) {
            int v = G[u][j].to, d = G[u][j].cost;
            dis[v] = min(dis[v], d);
        }
    }
}

int main()
{
    cin >> n >> m;
    memset(dis, 0x3f, sizeof(dis));
    for (int i = 0; i < m; ++i) {
        int from, to, cost;
        cin >> from >> to >> cost;
        G[from].push_back(Node(to, cost));
        G[to].push_back(Node(from, cost));
    }
    Prim();
    for (size_t i = 1; i < ans.size(); ++i)
        cout << ans[i] << ' ';
}

一句話Tarjan演算法

新時間戳遇上了舊時間戳,就連成了環,dfn和low相同,則為環(或單點)的起點,維護dfn和low是要點。

tip:單向圖

#include <iostream>
#include <vector>
#include <stack>
using namespace std;

const int maxn = 10005;
int n, m;
bool ins[maxn] = {};
vector<int> G[maxn];
stack<int> S;
int dfn[maxn] = {}, low[maxn];
int index = 0, ans = 0;

void tarjan(int u)
{
    S.push(u);
    ins[u] = true;
    dfn[u] = low[u] = ++index;
    for (size_t i = 0; i < G[u].size(); ++i) {
        int v = G[u][i];
        if (dfn[v] == 0) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if (ins[v]) low[u] = min(low[u], low[v]);
    }
    if (dfn[u] == low[u]) {
        ans++;
        int now;
        do {
            now = S.top();
            S.pop();
            ins[now] = false;
            cout << now << ' ';
        } while (now != u);
        cout << endl;
    }
}

int main()
{
    cin >> n >> m;
    for (int i = 0; i < m; ++i) {
        int from, to;
        cin >> from >> to;
        G[from].push_back(to);  // 有向圖
    }
    for (int i = 1; i <= n; ++i) {
        if (!dfn[i]) tarjan(i);
    }
    cout << "ans = " << ans << endl;
}

一句話Euler迴路演算法

斷奇偶,一點出,棧結構,末尾入,走去邊,倒著輸。

#include <iostream>
#include <vector>
#include <stack>
using namespace std;

const int maxn = 1005;
int n, m;
bool G[maxn][maxn] = {};
stack<int> S;

void Euler(int u)
{
    cout << "u = " << u << endl;
    for (size_t i = 1; i <= n; ++i) {
        if (G[u][i]) {
            G[u][i] = G[i][u] = false;
            Euler(i);
        }
    }
    S.push(u);
}

int main()
{
    cin >> n >> m;
    for (int i = 0; i < m; ++i) {
        int from, to;
        cin >> from >> to;
        G[from][to] = G[to][from] = true;
    }
    Euler(4);
    while (!S.empty()) {
        cout << S.top() << ' ';
        S.pop();
    }
}

一句話floyd演算法

遍歷每個點作為中間結點,如果可以以此點更新兩鄰點,則更新。

#include <iostream>
#include <cstring>
using namespace std;

const int maxn = 1003;
int n, m;
int G[maxn][maxn] = {};

int main()
{
    memset(G, 0x3f, sizeof(G));
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        G[i][i] = 0;
    for (int i = 0; i < m; ++i) {
        int from, to, cost;
        cin >> from >> to >> cost;
        G[from][to] = G[to][from] = cost;
    }
    for (int k = 1; k <= n; ++k) {
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                G[i][j] = min(G[i][j], G[i][k] + G[k][j]);
            }
        }
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j)
            cout << G[i][j] << ' ';
        cout << endl;
    }
}

一句話DAG最長路演算法

預知後事如何,請從前事算起,取最大值也。

#include <iostream>
#include <vector>
using namespace std;

const int maxn = 1005;
int n, m;
int G[maxn][maxn] = {};
int dis[maxn] = {};

int DAG(int u)
{
    if (dis[u]) return dis[u];
    for (int i = 1; i <= n; ++i) {
        if (G[i][u]) {
            dis[u] = max(dis[u], DAG(i) + G[i][u]);
        }
    }
    return dis[u];
}

int main()
{
    cin >> n >> m;
    for (int i = 0; i < m; ++i) {
        int from, to, cost;
        cin >> from >> to >> cost;
        G[from][to] = cost;
    }
    DAG(4);
    for (int i = 1; i <= 5; ++i) {
        cout << dis[i] << ' ';
    }
    cout << endl;
}