最短路徑三種解法


基礎最短路模板:

有 n 個人,他們的編號為 1~n,其中有一些人相互認識,現在 x 想要認識 y,可以通過他所認識的人來認識更多的人
(如果 x 認識 y、y 認識 z,那么 x 可以通過 y 來認識 z),求出 x 最少需要通過多少人才能認識 y。
【輸入格式】
第 1 行 3 個整數 n、x、y,n≤100,1≤x、y≤n。
接下來是一個 n×n 的鄰接矩陣,a[i][j]=1 表示 i 認識 j,a[i][j]=0 表示不認識。
保證 i=j 時,a[i][j]=0,並且 a[i][j]=a[j][i]。
【輸出格式】
輸出一行一個數,表示 x 認識 y 最少需要通過的人數。
【樣例輸入】

5 1 5
0 1 0 0 0
1 0 1 1 0
0 1 0 1 0
0 1 1 0 1
0 0 0 1 0
【樣例輸出】
2

#include<bits/stdc++.h>
using namespace std;
/*
    1.可以直接跑BFS

    2.無向圖最短路徑,因為只有輸入只有一次查詢,那么dijkstra,spfa都可以
            dijkstart:因為權值全都為1,所以就不用考慮堆優化.

    3.還有一種多源不含負權最短路徑,floyd 算法,時間復雜度0(n^3),填空題可以使用一下
            借用的是動態規划的思路

*/
int u[105][105];
int n,X,Y;

int bfs(){
    queue<pair<int,int>>que;//先進先出,對於每一個條路線需要記錄所走的路徑長度
    vector<int>judg(n+1,0);//標記數組
    que.push({X,0});

    while(!que.empty()){
        pair<int,int> k=que.front();
        que.pop();
        if(k.first==Y){
            return k.second-1;//訪問到時走的路程-1就是訪問的人數
        }
        if(judg[k.first])continue;//標記點,防止反復訪問同一個點
        judg[k.first]=1;
        for(int i=1;i<=n;i++){
            if(i!=k.first&&u[k.first][i]==1){
                if(!judg[i]){
                    que.push({i,k.second+1});
                }
            }
        }
    }

    return 0;
}

//單源最短路徑,不含負權
int dijkstra(int x){
    /*
        在權值不同的時候,每次取隊首的元素,優化到保證是里面最小的,需要把隊列改成優先隊列
    */
    vector<int>ans(n+1,INT_MAX/2);//ans[i]記錄x到i的距離
    vector<int>judg(n+1,0);//標記數組,保證點只被訪問一次

    queue<int>que;
    que.push(x);
    ans[x]=0;
    while(!que.empty()){
        int k=que.front();
        que.pop();
        if(judg[k])continue;//被標記過
        judg[k]=1;
        for(int i=1;i<=n;i++){
            //if 保證不是自己到自己  k->i存在邊
            if(k!=i&&u[k][i]==1&&ans[i]>ans[k]+1){//更新距離,如果x->i的距離大於x->k->i,就跟新
                ans[i]=ans[k]+1;
                if(!judg[i]){   //滿足沒有被訪問過,加入隊列
                    que.push(i);
                }
            }
        }
    }
    return ans[Y]-1;//

}

//包含負權,特判是否存在負環
int spfa(int x){
    vector<int>ans(n+1,INT_MAX/2);//ans[i]記錄x到i的距離,類似於dijkstra
    vector<int>judg(n+1,0);//標記數組,保證隊列內不存在重復的點
    vector<int>e(n+1,0);   //特判數組,表示每個點入棧多少次,如果超過總點數就說明存在負環,一直循環

    queue<int>que;
    que.push(x);//進隊
    ans[x]=0;
    e[x]++;
    while(!que.empty()){
        int k=que.front();
        que.pop();
        if(e[k]>n)return false;//存在負環,找不到答案
        judg[k]=false;//不存隊列中了

        for(int i=1;i<=n;i++){
            if(k!=i&&u[k][i]==1&&ans[i]>ans[k]+1){
                ans[i]=ans[k]+1;
                if(!judg[i]){  //不存在隊列,加入
                    judg[i]=true;
                    e[i]++;
                    que.push(i);
                }
            }
        }
    }
    return ans[Y]-1;

}


//多源最短路徑
int floyd(int x,int y){
    vector<vector<int>>ans(n+1,vector<int>(n+1,INT_MAX/2));//ans[i][j]表示i->j的距離
    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j++){
            if(u[i][j]){
                ans[i][j]=1;//所有的距離都是1
            }
        }
    }

    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                if(ans[i][j]>ans[i][k]+ans[k][j]){
                    ans[i][j]=ans[i][k]+ans[k][j];
                }
            }
        }
    }
    return ans[x][y]-1;
}

int main(){

    scanf("%d%d%d",&n,&X,&Y);

    //編號從1開始,那么所有的都按1
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&u[i][j]);
        }
    }

//    printf("%d",bfs());
//    printf("%d",dijkstra(X));//求x->到所有的距離
//    printf("%d",spfa(X));      //求x->到所有的距離
    printf("%d",floyd(X,Y)); //x->y的距離
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM