图论笔记1

教程来自代码随想录

1.1邻接矩阵储存法

//创建:二维数组
    vector<vector<int>> graph(n,vector<int>(n,0));
    
//储存
    for(int i=0;i<m;i++){
        int x1,x2;
        cin>>x1>>x2;
        graph[x1-1][x2-1]=1;
    }

1.2邻接表储存法

补充:c++中的list是链表 链接

//创建:数组+链表
vector<list<int>> graph(n + 1);

//输入
while (m--) {
    cin >> s >> t;
    // 使用邻接表 ,表示 s -> t 是相连的
    graph[s].push_back(t);
}
//读取数据
if (result.size() == 0) cout << -1 << endl;
    for (const vector<int> &pa : result) {
        for (int i = 0; i < pa.size() - 1; i++) {
            cout << pa[i] << " ";
        }
        cout << pa[pa.size() - 1]  << endl;
    }

2.1 dfs和bfs

所有可达路径

dfs三部曲:
确认递归函数和参数、确认终止条件、处理目前搜索节点的出发路径(处理节点、dfs递归、回溯)

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

vector<vector<int>> results;
vector<int> result;


//1 确认递归函数参数: 邻接表,当前历遍节点,终点
void dfs(const vector<vector<int>>& graph,int x,int n){
    //2 确认终止条件:当当前节点x = 最后一个节点n,就是从起点到了终点
    if(x==n-1){
        results.push_back(result);//存入结果
        return;
    }
    //3、处理目前搜索节点的出发路径
    for(int i=0;i<n;i++){
        if(graph[x][i]){            //找到下一个节点
            result.push_back(i+1);  //处理节点
            dfs(graph,i,n);         //处理下一个节点
            result.pop_back();      //利用vector的性质回溯
        }
    }
}

int main(){
    int n,m;
    cin>>n>>m;
    
    vector<vector<int>> graph(n,vector<int>(n,0));
    
    for(int i=0;i<m;i++){
        int x1,x2;
        cin>>x1>>x2;
        graph[x1-1][x2-1]=1;
    }
    
    
    result.push_back(1); //重要!先把开始节点放进去
    dfs(graph,0,n);
    
    if(results.size()==0)cout<<"-1"<<endl;
    for(int i=0;i<int(results.size());i++){
        for(int j=0;j<int(results[i].size()-1);j++){
            cout<<results[i][j]<<" ";
        }
        cout<<results[i][results[i].size()-1]<<endl;
    }
    
}

要注意0的问题。(开辟n个位置从0-n-1,开辟n+1个位置从0-n)

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

vector<vector<int>> results;
vector<int> result;


//1 确认递归函数参数: 邻接表,当前历遍节点,终点
void dfs(const vector<list<int>>& graph,int x,int n){
    //2 确认终止条件:当当前节点x = 最后一个节点n,就是从起点到了终点
    if(x==n-1){
        results.push_back(result);//存入结果
        return;
    }
    //3、处理目前搜索节点的出发路径
    for(int i:graph[x]){            //找到下一个节点
            result.push_back(i+1);  //处理节点
            dfs(graph,i,n);         //处理下一个节点
            result.pop_back();      //利用vector的性质回溯
    }
}

int main(){
    int n,m;
    cin>>n>>m;
    
    vector<list<int>> graph(n);
    
    for(int i=0;i<m;i++){
        int x1,x2;
        cin>>x1>>x2;
        graph[x1-1].push_back(x2-1);
    }
    
    
    result.push_back(1); //重要!先把开始节点放进去
    dfs(graph,0,n);
    
    if(results.size()==0)cout<<"-1"<<endl;
    for(int i=0;i<int(results.size());i++){
        for(int j=0;j<int(results[i].size()-1);j++){
            cout<<results[i][j]<<" ";
        }
        cout<<results[i][results[i].size()-1]<<endl;
    }
    
}

岛屿问题99

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

//1、确定参数
void dfs(const vector<vector<int>>& graph,vector<vector<int>>& visited, int x, int y){
   int dir[4][2]={0,1,  1,0,  -1,0,  0,-1};
    
    //2、终止条件(访问过了/周边都是0了,一条路就走完了)
    if(visited[x][y] || graph[x][y]==0) return;
    //3、处理当前点
    visited[x][y]=1;
    for(int i=0;i<4;i++){//上下左右四个位置,作用是感染同一岛屿的所有块(颜色填充也是这个原理)
        int newx=x+dir[i][0];
        int newy=y+dir[i][1];
        if(newx<0||newx>=int(graph.size())||newy<0||newy>=int(graph[0].size())) continue;
        dfs(graph,visited,newx,newy);
    }
    
}

int main(){
    int n,m;
    cin>>n>>m;
    vector<vector<int>> graph(n,vector<int>(m,0));
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>graph[i][j];
        }
    }
    
    // for(int i=0;i<n;i++){
    //     for(int j=0;j<m;j++){
    //         cout<<graph[i][j]<<" ";
    //     }
    //     cout<<endl;
    // }
    vector<vector<int>> visited(n,vector<int>(m,0));
    int result=0;
    
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(!visited[i][j] && graph[i][j]==1){
                dfs(graph,visited,i,j);
                result++;
            }
        }
    }
    cout<<result;
}

bfs

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

void bfs(queue<pair<int,int>>& q,const vector<vector<int>>& graph,vector<vector<int>>& visited,int x,int y){
   int dir[4][2]={0,-1, 0,1, -1,0, 1,0};
   
    q.push({x,y});
    visited[x][y]=1;//进队列就代表访问过了
    
    while(!q.empty()){//队列+while(!q.empty()是bfs的精髓
        pair<int,int> cur=q.front();//老天爷啊别忘了队列没有top
        q.pop();
        int curx=cur.first;
        int cury=cur.second;
        for(int i=0;i<4;i++){
            int newx=curx+dir[i][0];
            int newy=cury+dir[i][1];
             //这里是用continue跳过!!不是break
            if(newx<0||newx>=graph.size()||newy<0||newy>=graph[0].size()) continue;
            if(!visited[newx][newy] && graph[newx][newy]==1){
                q.push({newx,newy});
                visited[newx][newy]=1;//进队列就代表访问过了。这两行要始终欧冠绑定在一起。
            }
        }
    }
    
}
int main(){
    int n,m;
    cin>>n>>m;
    vector<vector<int>> graph(n,vector<int>(m,0));
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>graph[i][j];
        }
    }
    
    // for(int i=0;i<n;i++){
    //     for(int j=0;j<m;j++){
    //         cout<<graph[i][j]<<" ";
    //     }
    //     cout<<endl;
    // }
    vector<vector<int>> visited(n,vector<int>(m,0));
    queue<pair<int,int>> q;
    int result=0;
    
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(!visited[i][j] && graph[i][j]==1){
                bfs(q,graph,visited,i,j);
                result++;
                //cout<<i<<" "<<j<<endl;
            }
        }
    }
    cout<<result;
}

岛屿最大面积100

bfs

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

//1、确定参数(注意!!这里visited和single想要被改变,要传入变量本身!要用&)
void dfs(const vector<vector<int>>& graph,vector<vector<int>>& visited,int x,int y,int& single){
    //2、停止条件
    if(graph[x][y]==0||visited[x][y]){return;}
    
    //3、处理当前节点
    single++;
    visited[x][y]=1;
    int dir[4][2]={0,-1,0,1,-1,0,1,0};
    for(int i=0;i<4;i++){
        int newx=x+dir[i][0];
        int newy=y+dir[i][1];
        if(newx<0||newx>=int(graph.size())||newy<0||newy>=int(graph[0].size())) continue;
        dfs(graph,visited,newx,newy,single);
    }
}

int main(){
    int n,m;
    cin>>n>>m;

    vector<vector<int>> graph(n,vector<int>(m,0));
    
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>graph[i][j];
        }
    }
    
    int result=0;
    vector<int> s;
    vector<vector<int>> visited(n,vector<int>(m,0));
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(!visited[i][j] && graph[i][j]==1){
                int single=0;
                dfs(graph,visited,i,j,single);
                result=max(result,single);
            }
        }
    }
    
    cout<<result;
    
    
}

bfs

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


void dfs(const vector<vector<int>>& graph,vector<vector<int>>& visited,int x,int y,int& single){
    queue<pair<int,int>> q;
    int dir[4][2]={0,-1,0,1,-1,0,1,0};
    
    if(graph[x][y]==0||visited[x][y]){return;}
    
    q.push({x,y});
    visited[x][y]=1; 
    single++;
    
    while(!q.empty()){
        pair<int,int> cur=q.front();
        q.pop();
        int curx=cur.first;
        int cury=cur.second;
        for(int i=0;i<4;i++){
            int newx=curx+dir[i][0];
            int newy=cury+dir[i][1];
            if(newx<0||newx>=int(graph.size())||newy<0||newy>=int(graph[0].size())) continue;
            if(!visited[newx][newy] && graph[newx][newy]==1){
                q.push({newx,newy});
                visited[newx][newy]=1; 
                single++;
            }
        } 
    }
    
}

int main(){
    int n,m;
    cin>>n>>m;

    vector<vector<int>> graph(n,vector<int>(m,0));
    
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>graph[i][j];
        }
    }
    
    int result=0;
    vector<int> s;
    vector<vector<int>> visited(n,vector<int>(m,0));
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(!visited[i][j] && graph[i][j]==1){
                int single=0;
                dfs(graph,visited,i,j,single);
                result=max(result,single);
            }
        }
    }
    
    cout<<result;
    
    
}

孤岛总面积101

关键就是先历遍边缘,全部变成0.再处理中间的孤岛
dfs

#include<iostream>
#include<vector>
using namespace std;
//1、定参数
void dfs(vector<vector<int>>& graph,vector<vector<int>>& visited,int x,int y,int& count){
    //2、终止条件
    if(graph[x][y]==0)return;
    count++;
    //3、处理当前节点
    graph[x][y]=0;//这里不一样,靠边的清零
    int dir[4][2] = {-1, 0, 0, -1, 1, 0, 0, 1};
    for (int i = 0; i < 4; i++) { // 向四个方向遍历
        int nextx = x + dir[i][0];
        int nexty = y + dir[i][1];
        // 超过边界
        if (nextx < 0 || nextx >= graph.size() || nexty < 0 || nexty >= graph[0].size()) continue;
        if (!visited[nextx][nexty] && graph[nextx][nexty]){
            dfs(graph,visited, nextx, nexty,count);
            visited[nextx][nexty]=1;
            graph[nextx][nexty]=0;
        }

        
    }
}

int main(){
    int n,m;
    cin>>n>>m;
    int count=0;
    vector<vector<int>> graph(n,vector<int>(m,0));
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>graph[i][j];
        }
    }
    
    vector<vector<int>> visited(n,vector<int>(m,0));
    
    //先把有靠边的块清零(这里注意行列分开)
    for(int i=0;i<n;i++){ //上限是n
        if(graph[i][0]==1)dfs(graph,visited,i,0,count);
        if(graph[i][m-1]==1)dfs(graph,visited,i,m-1,count);
    }
    for(int i=0;i<m;i++){ //上限是m
        if(graph[0][i]==1)dfs(graph,visited,0,i,count);
        if(graph[n-1][i]==1)dfs(graph,visited,n-1,i,count);
    }
    //---------------验证请完零没有----------------
    // for(int i=0;i<n;i++){
    //     for(int j=0;j<m;j++){
    //         cout<<graph[i][j];
    //     }
    //     cout<<endl;
    // }
    //----------------------------------------------
    
    // //再搜索
    count=0;
    
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(!visited[i][j]&&graph[i][j]==1){
                dfs(graph,visited,i,j,count);
            }
        }
    }
    cout<<count;
}

bfs(两者计算面积的方式不一样!看count的计数)

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

void bfs(vector<vector<int>>& graph,vector<vector<int>>& visited,int x,int y,int& count){
    queue<pair<int,int>> q;
    int dir[4][2] = {-1, 0, 0, -1, 1, 0, 0, 1};
    
    if(graph[x][y]==0)return;
    
    q.push({x,y});
    visited[x][y]=1;
    graph[x][y]=0;//这里不一样,靠边的清零
    count++;
    
    while(!q.empty()){
        pair<int,int> cur=q.front();
        q.pop();
        int curx=cur.first;
        int cury=cur.second;
        for (int i = 0; i < 4; i++) { // 向邻接的历遍
            int nextx = curx + dir[i][0];
            int nexty = cury + dir[i][1];
            // 超过边界
            if (nextx < 0 || nextx >= graph.size() || nexty < 0 || nexty >= graph[0].size()) continue;
            if (!visited[nextx][nexty] && graph[nextx][nexty]){
                q.push({nextx,nexty});
                visited[nextx][nexty]=1;
                graph[nextx][nexty]=0;
                count++;
            }
        }
    }
}

int main(){
    int n,m;
    cin>>n>>m;
    int count=0;
    vector<vector<int>> graph(n,vector<int>(m,0));
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>graph[i][j];
        }
    }
    
    vector<vector<int>> visited(n,vector<int>(m,0));
    
    //先把有靠边的块清零(这里注意行列分开)
    for(int i=0;i<n;i++){ //上限是n
        if(graph[i][0]==1)dfs(graph,visited,i,0,count);
        if(graph[i][m-1]==1)dfs(graph,visited,i,m-1,count);
    }
    for(int i=0;i<m;i++){ //上限是m
        if(graph[0][i]==1)dfs(graph,visited,0,i,count);
        if(graph[n-1][i]==1)dfs(graph,visited,n-1,i,count);
    }
    //---------------验证请完零没有----------------
    // for(int i=0;i<n;i++){
    //     for(int j=0;j<m;j++){
    //         cout<<graph[i][j];
    //     }
    //     cout<<endl;
    // }
    //----------------------------------------------
    
    // //再搜索
    count=0;
    
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(!visited[i][j]&&graph[i][j]==1){
                dfs(graph,visited,i,j,count);
            }
        }
    }
    cout<<count;
}

计算节点处理时长

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

//1、参数
void dfs(vector<vector<int>>& graph, vector<bool>& visited, vector<int>& start, vector<int>& end, int n, int& time,int node){
    visited[node]=true;
    start[node]=++time;
    
    
    //3、处理节点
    for(int to=1;to<=n;to++){
        if(graph[node][to]&& !visited[to]){
            dfs(graph,visited,start,end,n,time,to);
        }
    }
    end[node] = ++time;
}

int main(){
    int N;
    cin>>N;
    
    while(N--){
        int n,m;
        cin>>n>>m;

        vector<vector<int>> graph(n+1,vector<int>(n+1,0));
        for(int i=0;i<m;i++){
            int x,y;
            cin>>x>>y;
            graph[x][y]=1;
            graph[y][x]=1;
        }

        //检查用
        // for(int i=1;i<=n;i++){
        //     for(int j=1;j<=n;j++){
        //         cout<<graph[i][j]<<" ";
        //     }
        //     cout<<endl;
        // }

        vector<bool> visited(n+1,false);
        vector<int> start(n+1,0);
        vector<int> end(n+1,0);
        int time=0;

        for(int node=1;node<=n;node++){
            if(!visited[node])dfs(graph,visited,start,end,n,time,node);
        }
        

        for(int j=1;j<=n;j++){
            cout<<j<<":"<<start[j]<<"-"<<end[j]<<endl;
        }
        cout<<"---"<<endl;
    }
}

读文献问题

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

void dfs(const vector<vector<int>>& graph, vector<bool>& visited, int node, vector<int>& result) {
    visited[node] = true;
    result.push_back(node);

    // 对邻居节点排序以确保按编号顺序访问
    const vector<int>& neighbors = graph[node];
    vector<int> sorted_neighbors(neighbors.begin(), neighbors.end());
    sort(sorted_neighbors.begin(), sorted_neighbors.end());

    for (int neighbor : sorted_neighbors) {
        if (!visited[neighbor]) {
            dfs(graph, visited, neighbor, result);
        }
    }
}

void bfs(const vector<vector<int>>& graph, vector<bool>& visited, vector<int>& result) {
    queue<int> q;
    q.push(1);
    visited[1] = true;

    while (!q.empty()) {
        int current = q.front();
        q.pop();
        result.push_back(current);

        // 对邻居节点排序以确保按编号顺序访问
        const vector<int>& neighbors = graph[current];
        vector<int> sorted_neighbors(neighbors.begin(), neighbors.end());
        sort(sorted_neighbors.begin(), sorted_neighbors.end());

        for (int neighbor : sorted_neighbors) {
            if (!visited[neighbor]) {
                visited[neighbor] = true;
                q.push(neighbor);
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false); // 加快输入输出速度
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;

    vector<vector<int>> graph(n + 1);
    for (int i = 0; i < m; ++i) {
        int u, v;
        cin >> u >> v;
        graph[u].push_back(v);
    }

    // DFS 遍历
    vector<bool> visited_dfs(n + 1, false);
    vector<int> dfs_result;
    dfs(graph, visited_dfs, 1, dfs_result);

    // 输出 DFS 结果
    for (size_t i = 0; i < dfs_result.size(); ++i) {
        cout << dfs_result[i];
        if (i != dfs_result.size() - 1) cout << " ";
    }
    cout << endl;

    // BFS 遍历
    vector<bool> visited_bfs(n + 1, false);
    vector<int> bfs_result;
    bfs(graph, visited_bfs, bfs_result);

    // 输出 BFS 结果
    for (size_t i = 0; i < bfs_result.size(); ++i) {
        cout << bfs_result[i];
        if (i != bfs_result.size() - 1) cout << " ";
    }
    cout << endl;

    return 0;
}

2.2 最小生成树

prim

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

int main(){
    int v,e;
    cin>>v>>e;
    
    vector<vector<int>> graph(v+1,vector<int>(v+1,100001));
    
    while(e--){
        int x1,x2,val;
        cin>>x1>>x2>>val;
        graph[x1][x2]=val;//是无向图
        graph[x2][x1]=val;
    }
    //------------------检查用----------------------------
    // for(int i=1;i<=v;i++){
    //     for(int j=1;j<=v;j++){
    //         cout<<graph[i][j]<<" ";
    //     }
    //     cout<<endl;
    // }
    //----------------------------------------------------
    
    vector<int> minDist(v+1,100001);
    vector<int> intree(v+1,false);
    vector<int> arc(v+1);
    int start=-1;
   
    for(int count=1;count<v;count++){//循环n-1次
        //1、选距离生成树最近的节点,即更新start
        int min=INT_MAX;
        for(int i=1;i<=v;i++){
            if(!intree[i] && minDist[i]<min) {//这里是算生成树的每个节点与离它最近的树外节点的距离
                min=minDist[i];
                start=i;
            }
        }
        
        if(start==-1) {cout<<"-1"<<endl;return 0;}
        //把最近的节点加入生成树
        intree[start]=1;
        
        //------------------检查用----------------------------
        // for(int i=1;i<=v;i++){
        //     cout<<minDist[i]<<" ";
        // }
        // cout<<"###################"<<endl;
        //----------------------------------------------------
        
        //3、更新非生成树节点到生成树的距离,即minDist数组。并记录1中最短边,即更新arc数组
        for(int j=1;j<=v;j++){
            if(!intree[j] && graph[start][j]<minDist[j]){//只要一端在start,另一端不在树里,且值比当前位置值小的,都放进来
                minDist[j]=graph[start][j];
                arc[j]=start;
            }
        }
        
        //------------------检查用----------------------------
        // for(int i=1;i<=v;i++){
        //     cout<<minDist[i]<<" ";
        // }
        // cout<<"-------------------"<<endl;
        //----------------------------------------------------
        
        
    }
    
     int total=0;
    for(int i=2;i<=v;i++){
       total+=minDist[i];
    }
    cout<<total<<endl;
}

2.3 最短路径

如果是无向图的话,在graph输入的时候记住要同时输入graph[x][y]和graph[y][x]
主要是dijkstra算法

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

int main(){
    int n,m;
    cin>>n>>m;
    
    vector<vector<int>> graph(n+1,vector<int>(n+1,INT_MAX));
    
    for(int i=1;i<=m;i++){
        int x,y,v;
        cin>>x>>y>>v;
        graph[x][y]=v;
        //cout<<graph[x][y];
    }
    
    vector<int> minDist(n+1,INT_MAX);
    vector<bool> visited(n+1,false);
    
    int start=1;
    int cur=start;
    minDist[start]=0;
    
    for(int i=0;i<n;i++){
        int minVal=INT_MAX;
        
         // 1、选距离源点最近且未访问过的节点
        for(int j=1;j<=n;j++){
            if(!visited[j] && minDist[j]<minVal){
                minVal=minDist[j];
                cur=j;
            }
        }
        
         // 2、标记该节点已被访问
        visited[cur]=true;
        
        // 3、第三步,更新非访问节点到源点的距离(即更新minDist数组)
        for(int j=1;j<=n;j++){
            if(!visited[j] && graph[cur][j]!=INT_MAX && minDist[cur]+graph[cur][j]<minDist[j]){
                minDist[j]=minDist[cur]+graph[cur][j];
            }
        }
        
        // ---------------------打印日志检查----------------------------------
        //cout << "select:" << cur << endl;
        //for (int v = 1; v <= n; v++) cout <<  v << ":" << minDist[v] << " ";
        //cout << endl << endl;;
    }
    
    int end=n;
    if(minDist[end]==INT_MAX)cout<<"-1";
    else cout<<minDist[end];
}

单源最短路径

#include<iostream>
#include<climits>
#include<vector>

using namespace std;

int main(){
    int n,m;
    cin>>n>>m;
    
    vector<vector<int>> graph(n+1,vector<int>(n+1,INT_MAX));

    
    for(int i=0;i<m;i++){
        int x,y,v;
        cin>>x>>y>>v;
        graph[x][y]=v;
        //cout<<graph[x][y]<<endl;
    }

    vector<int> minDist(n+1,INT_MAX);
    vector<bool> visited(n+1,false);

    int start=1;
    int cur=start;
    minDist[start]=0;

    for(int i=0;i<n;i++){
        int minVal=INT_MAX;
        
        
        //1、选距离源点最近且未访问过的节点
        for(int j=1;j<=n;j++){
            if(!visited[j] && minDist[j]<minVal){
                minVal=minDist[j];
                cur=j;
            }
        }

        //2、标记选择的节点变为访问过
        visited[cur]=true;

        //3、更新minDist数组的值
        for(int j=1;j<=n;j++){
            if(!visited[j] && graph[cur][j]!=INT_MAX && minDist[cur]+graph[cur][j]<minDist[j]){
                minDist[j]=minDist[cur]+graph[cur][j];//在选中的最短点(cur)的基础上计算最短路径,并更新更短的部分。
            }
        }

        // //-------------------验证用--------------------------------
        // cout<<"Current node: "<<cur<<endl;
        // for(int j=1;j<=n;j++){
        //     cout<<minDist[j]<<" ";
        // }
        // cout<<endl;
    }

    //打印结果
    for(int end=1;end<=n;end++){
        if(minDist[end]==INT_MAX) cout<<"-1 ";
        else cout<<minDist[end]<<" ";
    }
    
}

全源最短路径
值得注意的是每次的minDist管一行用(即从start到1-n所有顶点)。

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

int n,m;
//vector<vector<int>> graph;

void dijs(int start,vector<vector<int>> graph){
    vector<int> minDist(n+1,INT_MAX);
    vector<bool> visited(n+1,false);

    minDist[start]=0;
    
    int cur=start;
    for(int i=1;i<=n;i++){
        int minVal=INT_MAX;

        //1、找到最小点
        for(int j=1;j<=n;j++){
            if(!visited[j] && minDist[j]<minVal){
                minVal=minDist[j];
                cur=j;
            }
        }
         
        //2、标记该点被访问过
        visited[cur]=true;

        //3、更新minDist
        for(int j=1;j<=n;j++){
            if(!visited[j] && graph[cur][j]!=INT_MAX && minDist[cur]+graph[cur][j]<minDist[j]){
                minDist[j]=minDist[cur]+graph[cur][j];
            }
        }
        
    }
 
    for(int end=1;end<=n;end++){
        if(minDist[end]==INT_MAX) cout<<-1<<" ";
        else cout<<minDist[end]<<" ";
    }
    
}

int main(){
    //int n,m;
    cin>>n>>m;
    //cout<<n<<m;
    vector<vector<int>> graph(n+1,vector<int>(n+1,INT_MAX));

    for(int i=0;i<m;i++){
        int x,y,v;
        cin>>x>>y>>v;
        graph[x][y]=v;
        //cout<<graph[x][y];
    }
 
    for(int start=1;start<=n;start++){
        dijs(start,graph);
        cout<<endl;
    }
}

对于稀疏图,用最小堆

#include <iostream>
#include <vector>
#include <list>
#include <queue>
#include <climits>
using namespace std; 
// 小顶堆
class mycomparison {
public:
    bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
        return lhs.second > rhs.second;
    }
};
// 定义一个结构体来表示带权重的边
struct Edge {
    int to;  // 邻接顶点
    int val; // 边的权重

    Edge(int t, int w): to(t), val(w) {}  // 构造函数
};

int main() {
    int n, m, p1, p2, val;
    cin >> n >> m;

    vector<list<Edge>> grid(n + 1);

    for(int i = 0; i < m; i++){
        cin >> p1 >> p2 >> val; 
        // p1 指向 p2,权值为 val
        grid[p1].push_back(Edge(p2, val));

    }

    int start = 1;  // 起点
    int end = n;    // 终点

    // 存储从源点到每个节点的最短距离
    std::vector<int> minDist(n + 1, INT_MAX);

    // 记录顶点是否被访问过
    std::vector<bool> visited(n + 1, false); 
    
    // 优先队列中存放 pair<节点,源点到该节点的权值>
    priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pq;


    // 初始化队列,源点到源点的距离为0,所以初始为0
    pq.push(pair<int, int>(start, 0)); 
    
    minDist[start] = 0;  // 起始点到自身的距离为0

    while (!pq.empty()) {
        // 1. 第一步,选源点到哪个节点近且该节点未被访问过 (通过优先级队列来实现)
        // <节点, 源点到该节点的距离>
        pair<int, int> cur = pq.top(); pq.pop();

        if (visited[cur.first]) continue;

        // 2. 第二步,该最近节点被标记访问过
        visited[cur.first] = true;

        // 3. 第三步,更新非访问节点到源点的距离(即更新minDist数组)
        for (Edge edge : grid[cur.first]) { // 遍历 cur指向的节点,cur指向的节点为 edge
            // cur指向的节点edge.to,这条边的权值为 edge.val
            if (!visited[edge.to] && minDist[cur.first] + edge.val < minDist[edge.to]) { // 更新minDist
                minDist[edge.to] = minDist[cur.first] + edge.val;
                pq.push(pair<int, int>(edge.to, minDist[edge.to]));
            }
        }

    }

    if (minDist[end] == INT_MAX) cout << -1 << endl; // 不能到达终点
    else cout << minDist[end] << endl; // 到达终点最短路径
}

2.4 拓扑排列

软件构建

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

int main(){
    int n,m;
    cin>>n>>m;
    
    vector<int> inDegree(n,0);
    unordered_map<int,vector<int>> umap;//配对:每个顶点+该顶点关联的向量组
    
    while(m--){
        int s,t;
        cin>>s>>t;
        
        inDegree[t]++;
        umap[s].push_back(t);
    }
    
    queue<int> q;//这个q是辅助工具
    vector<int> result;
    for(int i=0;i<n;i++){
        if(inDegree[i]==0)q.push(i);
    }
    
    while(!q.empty()){
        int cur=q.front();
        q.pop();
        result.push_back(cur);//1、入度为0的加入结果集
        vector<int> v=umap[cur]; //找到该点对应的依赖节点串

        if(!v.empty()){
            for(int i=0;i<v.size();i++){
                inDegree[v[i]]--;//2、从表中删除点。(形式上只是把入度-1
                if(inDegree[v[i]]==0)q.push(v[i]);
            }
        }
    }
    if(result.size()==n){
        for(int i=0;i<n-1;i++){
            cout<<result[i]<<" ";
        }
        cout<<result[n-1];
    }else{
        cout<<"-1"<<endl;
    }
}

食物链条数

#include<iostream>
#include<vector>
#include<queue>
#include <unordered_map>

using namespace std;
const int MOD = 80112002;
int main(){
    int n,m;
    cin>>n>>m;

    vector<int> inDegree(n+1,0),outDegree(n + 1, 0);
    unordered_map<int,vector<int>> umap;
    queue<int> q;
    vector<long long> pathCounts(n + 1, 0);//一个数组来存储到达每个节点的路径数量

    for(int i=0;i<m;i++){
        int u,v;
        cin>>u>>v;
        inDegree[v]++;
        outDegree[u]++;
        umap[u].push_back(v);
    }

    for(int i=1;i<=n;i++){
        if(inDegree[i]==0) {
            q.push(i);
            pathCounts[i]=1;//初始化 每个开头都至少有一条
        }
    }

    while(!q.empty()){
        int cur=q.front();
        q.pop();
        
        //不需要排序结果
        // for(int j=0;j<v.size();j++){
        //     inDegree[v[i]]--;
        //     if(inDegree[v[i]]==0)q.push(v[i]);
        // }

        //而是计算排序可能的种数
        //先计算从每个点开始,到最终的种数
        for (int next : umap[cur]) {
            pathCounts[next] = (pathCounts[next] + pathCounts[cur]) % MOD;
            if (--inDegree[next] == 0) {
                q.push(next);
            }
        }
    }
        // 再计算所有消费者的和(即为最终解):将所有出度为0的节点(即消费者)的路径数量相加
        long long result = 0;
        for (int i = 1; i <= n; ++i) {
            if (outDegree[i] == 0) { // 出度为0意味着是消费者
                result = (result + pathCounts[i]) % MOD; //加和在这里
            }
        }

        cout << result << endl;
    

    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值