1. 程式人生 > 實用技巧 >圖演算法(1)

圖演算法(1)

所有節點是從1到N號,並使用鄰接矩陣儲存

拓撲排序

int inDegree[MAXV];
bool topSort() {
    queue<int> q;
    int cnt = 0;
    for (int i = 1; i <= N; ++i) {
        if (!inDegree[i]) q.push(i);
    }
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int v = 1; v <= N; ++v) {
            if (Graph[u][v] != INF) {
                --inDegree[v];
                if (!inDegree[v]) q.push(v);
            }
        }
        ++cnt;
    }
    if (cnt != N) return false;
    return true;
}

最短路徑

Floyd

int dis[MAXV][MAXV];
void Floyd() {
    for (int k = 1; k <= N; ++k) {
        for (int u = 1; u <= N; ++u) {
            for (int v = 1; v <= N; ++v) {
                if (dis[u][k] != INF && dis[k][v] != INF && dis[u][k] + dis[k][v] < dis[u][v])
                    dis[u][v] = dis[u][k] + dis[k][v];
            }
        }
    }
}

dijkstra

使用幾種不同的優先順序尋找單源最短路徑,使用dijkstra + DFS最方便

vector<int> pre[MAXV];
int Graph[MAXV][MAXV], N, d[MAXV];
bool vis[MAXV];
int s;

void dijkstra() {
    using pr = pair<int, int>;
    // 堆優化尋找最小值
    priority_queue<pr, vector<pr>, greater<pr> > heap;
    fill(d, d + MAXV, INF);
    fill(vis, vis + MAXV, false);
    heap.emplace(0, s);
    for (int i = 1; i <= N; ++i) heap.emplace(INF, i);
    for (int i = 0; i < N; ++i) {
        while (vis[heap.top().second]) heap.pop();
        int u = heap.top().second;
        d[u] = heap.top().first;
        vis[u] = true;
        heap.pop();
        for (int v = 1; v <= N; ++v) {
            if (!vis[v] && Graph[u][v] != INF) {
                if (d[u] + Graph[u][v] < d[v]) {
                    d[v] = d[u] + Graph[u][v];
                    heap.emplace(d[v], v);
                    pre[v].clear();
                    pre[v].push_back(u);
                } else if (d[u] + Graph[u][v] == d[v]) {
                    pre[v].push_back(u);
                }
            }
        }
    }
}

int opt;
vector<int> path, tmpPath;

void DFS(int v) {
    tmpPath.push_back(v);
    if (v == s) {
        int tmpOpt;
        // 計算 tmpOpt
        if (tmpOpt < opt) {
            opt = tmpOpt;
            path = tmpPath;
        }
    } else
        for (auto &i : pre[v]) DFS(i);
    tmpPath.pop_back();
}

SPFA

之後補

最小生成樹

稠密圖用prim,稀疏圖用dijkstra

prim

// 和dijkstra寫法類似,只是d[u]的含義表示到生成樹集合的距離
void prim() {
	int ans = 0;
    using pr = pair<int, int>;
    priority_queue<pr, vector<pr>, greater<pr> > heap;
    fill(d, d + MAXV, INF);
    fill(vis, vis + MAXV, false);
    heap.emplace(0, s);
    for (int i = 1; i <= N; ++i) heap.emplace(INF, i);
    for (int i = 0; i < N; ++i) {
        while (vis[heap.top().second]) heap.pop();
        int u = heap.top().second;
        d[u] = heap.top().first;
        ans += d[u];
        vis[u] = true;
        heap.pop();
        for (int v = 1; v <= N; ++v) {
            if (!vis[v] && Graph[u][v] != INF && Graph[u][v] < d[v]) {
                d[v] = Graph[u][v];
                heap.emplace(d[v], v);
            }
        }
    }
}

kruskal

const int MAXE = 1000;
int father[MAXV];
struct edge {
    int u, v, weight;
};
edge e[MAXE];
int findFather(int u) {
    if (father[u] == u)
        return u;
    else {
        int f = findFather(father[u]);
        father[u] = f;
        return f;
    }
}
int kruskal() {
    father[0] = 0;
    iota(father + 1, father + MAXV, 1);
    int ans = 0, cntEdge = 0;
    for (int i = 0; i < N; ++i) {
        int a = findFather(e[i].u), b = findFather(e[i].v);
        if (a != b) {
            father[a] = b;
            ans += e[i].weight;
            ++cntEdge;
        }
        if (cntEdge == N - 1) break;
    }
    if (cntEdge != n - 1) return -1;
    return ans;
}

關鍵路徑

之後再補