一句话图论算法_8种

版权声明:看我干嘛? 你又没打算转载我的博客~ https://blog.csdn.net/wjh2622075127/article/details/82347224

一句话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;
}

猜你喜欢

转载自blog.csdn.net/wjh2622075127/article/details/82347224