图论学习笔记

图论

以下只考虑简单图:两个节点之间同方向至多有一条边。

最短路径

单源最短路径

T ( n ) = Θ ( ∣ E ∣ ⋅ T d k + ∣ V ∣ ⋅ T e m ) T(n)=\Theta(|E| \cdot T_{dk}+|V| \cdot T_{em}) T(n)=Θ(ETdk+VTem)
LeetCode743
朴素dijkstra算法
OSPF协议(链路状态算法)使用dijkstra算法。
松弛在一个可随机访问的数组里, T d k = O ( 1 ) T_{dk}=O(1) Tdk=O(1)。线性搜索并弹出最小节点所需时间复杂度为 T e m = O ( ∣ V ∣ ) T_{em}=O(|V|) Tem=O(V)
因此整个算法时间复杂度为 Θ ( ∣ V ∣ 2 ) \Theta(|V|^2) Θ(V2)

class Solution {
public:
    int networkDelayTime(vector<vector<int>>& times, int n, int k) {
        int res = -1;
        unordered_map<int, int> unvisited;
        vector<int> dist(n+1, INT_MAX);
        dist[k]=0;
        vector<vector<pair<int,int>>> adjacent_link(n+1, vector<pair<int,int>>());
        for(int i = 1; i <= n; i++){
            if (i==k) continue;
            unvisited.insert({i, INT_MAX});
        }
        for(const auto& edge:times){
            adjacent_link[edge[0]].push_back({edge[1], edge[2]});
            if(edge[0]==k){
                unvisited[edge[1]]=edge[2];
                dist[edge[1]]=edge[2];
            }
        }
        while(!unvisited.empty()){
            pair<int,int> cur = {-1, INT_MAX};
            for(const auto& vertex:unvisited){
                if(vertex.second<cur.second){
                    cur = vertex;
                }
            }
            if(cur.second==INT_MAX){
                return -1;
            }
            unvisited.erase(cur.first);
            for(auto&adjacent:adjacent_link[cur.first]){
                if(!unvisited.count(adjacent.first)) continue;
                if(adjacent.second!=INT_MAX&&unvisited[adjacent.first]>adjacent.second+cur.second){
                    unvisited[adjacent.first]=adjacent.second+cur.second;
                    dist[adjacent.first] = unvisited[adjacent.first];
                }
            }
        }
        for(int i = 1; i <= n; i++){
            res = dist[i]>res?dist[i]:res;
        }
        return res;
    }
};

堆优化dijkstra算法
松弛需要在一个优先级队列里进行入队,弹出需要恢复队列结构,两者都是 O ( log ⁡ ( ∣ V ∣ ) ) O(\log(|V|)) O(log(V))
因此整个算法时间复杂度为 Θ ( ( ∣ V ∣ + ∣ E ∣ ) ⋅ log ⁡ ( ∣ V ∣ ) ) \Theta((|V|+|E|) \cdot \log(|V|)) Θ((V+E)log(V))
适合稀疏图、稠密图复杂性更高 Θ ( ∣ V ∣ 2 ⋅ log ⁡ ( ∣ V ∣ ) ) \Theta(|V|^2 \cdot \log(|V|)) Θ(V2log(V))

class Solution {
public:
    int networkDelayTime(vector<vector<int>>& times, int n, int k) {
        int res = -1;
        unordered_set<int> visited;
        priority_queue<pair<int,int>,vector<pair<int,int>>,greater<>> pq;
        vector<int> dist(n+1, INT_MAX);
        dist[k]=0;
        vector<vector<pair<int,int>>> adjacent_link(n+1, vector<pair<int,int>>());
        for(const auto& edge:times){
            adjacent_link[edge[0]].push_back({edge[1], edge[2]});
        }
        pq.push({0,k});
        while(!pq.empty()){
            pair<int,int> cur = {-1, INT_MAX};
            cur = pq.top(); pq.pop();
            if(cur.second==INT_MAX){
                return -1;
            }
            if(visited.count(cur.second)) continue;
            for(auto&adjacent:adjacent_link[cur.second]){
                if(adjacent.second!=INT_MAX&&dist[adjacent.first]>adjacent.second+cur.first){
                    dist[adjacent.first] = adjacent.second+cur.first;
                    pq.push({dist[adjacent.first], adjacent.first});
                }
            }
            visited.insert(cur.second);
        }
        for(int i = 1; i <= n; i++){
            res = dist[i]>res?dist[i]:res;
        }
        if(res==INT_MAX){
            return -1;
        }else{
            return res;
        }
    }
};

有负环的最短路径 Bellman-Ford算法
Bellman-Ford方程: D i s ( s , t ) = min ⁡ v ∈ a d j a ( s ) { W ( s , v ) + D i s ( v , t ) } Dis(s,t)=\min_{v \in adja(s)}\{W(s,v)+Dis(v,t)\} Dis(s,t)=minvadja(s){W(s,v)+Dis(v,t)}
RIP协议(距离向量算法)使用Bellman-Ford方程。
未优化DP表维度版本:

class Solution {
public:
    int networkDelayTime(vector<vector<int>>& times, int n, int k) {
        int n_edges = times.size();
        vector<vector<int>> memo(n + 1, vector<int>(n + 1, INT_MAX));
        memo[k][0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++){
                memo[j][i]=memo[j][i-1];
            }
            for (int j = 1; j <= n_edges; j++) {
                const auto& edge = times[j - 1];
                int u = edge[0], v = edge[1], w = edge[2];
                if (memo[u][i - 1] != INT_MAX) {
                    memo[v][i] = memo[v][i] <= (memo[u][i - 1] + w)
                                 ? memo[v][i]
                                 : (memo[u][i - 1] + w);
                }
            }
        }
        int res = 0;
        for (int i = 1; i <= n; i++) {
            res = res > memo[i][n] ? res : memo[i][n];
        }
        return res == INT_MAX ? -1 : res;
    }
};

优化DP表维度版本:
多数直觉上 2 2 2维的DP,如果不需要回溯给出具体方案,可以改为滚动DP。例如背包问题。


多源最短路径与有负权边 Floyd算法

可以处理负边但不能处理负环,复杂度为 O ( ∣ V ∣ 3 ) O(|V|^3) O(V3)。每次考虑以前 k k k个为中介点,对所有 i , j i,j i,j节点对进行松弛。矩阵上一次的值表示前 k − 1 k-1 k1个为中介点的结果,第k次只需比较是否使用节点 k k k的情况。使用DP思想。
对角线在松弛中非负即代表出现了负环。
用prev[][]存储最短路径树即可进行path的重构。prev[u][v]存储u~>v路径上的倒数第二个节点。

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <unordered_set>
#include <climits>
#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> grid;
int main(){
    string line;
    std::getline(cin, line);
    istringstream iss(line);
    int m, n;
    vector<int>stations;
    vector<int>communities;
    iss >> m >> n;
    grid = vector<vector<int>>(m, vector<int>(n, 0));
    for(int i = 0; i < m; i++){
        std::getline(cin, line);
        istringstream iss(line);
        for(int j = 0; j < n; j++){
            iss >> grid[i][j];
            if(grid[i][j]==0){
                stations.push_back(i*n+j);
            }
            if(grid[i][j]==1){
                communities.push_back(i*n+j);
            }
        }
    }
    int num_vertices=m*n;
    vector<vector<int>> min_dist(num_vertices, vector<int>(num_vertices, INT_MAX));
    for(int i = 0; i < num_vertices; i++){
        for(int j = 0; j < num_vertices; j++){
            int row_i=i/n, col_i=i%n, row_j=j/n, col_j=j%n;
            if (i==j) {
                min_dist[i][j]=0;
            }else{
                if(grid[row_i][col_i]!=-1&&grid[row_j][col_j]!=-1){
                    if((row_i==row_j&&abs(col_i-col_j)==1)||(abs(row_i-row_j)==1&&col_i==col_j)){
                        min_dist[i][j]=1;
                        min_dist[j][i]=1;
                    }
                }
            }
        }
    }
    for(int k = 0; k < num_vertices; k++){
        for(int i = 0; i < num_vertices; i++){
            for(int j = 0; j < num_vertices; j++){
                if(min_dist[i][k]==INT_MAX || min_dist[k][j]==INT_MAX){
                    continue;
                }
                int tmp = min_dist[i][k]+min_dist[k][j];
                if(min_dist[i][j]>tmp){
                    min_dist[i][j]=tmp;
                }
            }
        }
    }
    int res = 0;
    for(const int &community:communities){
        int length_path=INT_MAX;
        for(const int &station:stations){
            if(min_dist[community][station]<length_path){
                length_path=min_dist[community][station];
            }
        }
        if (length_path<INT_MAX){
            res+=length_path;
        }
    }
    cout << res;
    return 0;
}

环检测算法

最小生成树

prim算法

小树长大

kruskal算法

合并森林

拓扑排序

AOE问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值