최단 경로 알고리즘 (Floyd, Dijkstra, .Bellman-Ford)

최근에 최단 경로를 검토하고 내 인상을 강화하는 방법으로 블로그를 씁니다.

여기에 사진 설명 삽입
1. Floyd 알고리즘
가장 짧고 간단한 알고리즘이라고 생각하지만 일반적으로 복잡도가 상대적으로 높기 때문에 단순함은 좋지 않습니다.

* 핵심 아이디어 :
두 점 사이의 거리를 줄이려면 긴장을 풀 수있는 세 번째 꼭지점이 필요합니다.

* 특정 단계 :
정점 1 ~ n을 차례로 사용하여 두 점 사이의 거리를 이완합니다.

이 알고리즘은 비교적 간단하므로 코드를 직접 업로드하십시오.

#include<iostream>


using namespace std;

const int N=2000;
int main()
{
    
    
    
    int maps[N][N];
    for(int i=1;i<=N;i++)
        for(int j=1;j<=N;j++)
        {
    
    if(i==j)
            maps[i][j]=0;//起点和终点相同,路程为0
         else
            maps[i][j]=INT_MAX;//一开始没有路,则全部是无穷大
        
        }
    int n,m;//假设有n条路,m个城市
    cin>>n;
    int a,b,c;
    for(int i=1;i<=n;i++)
    {
    
    cin>>a>>b>>c;
     maps[a][b]=c;
     maps[b][a]=c;//假设是双向路
     
        
    }
    for(int i=1;i<=m;i++)//枚举用来松弛的点
        for(int j=1;j<=m;j++)
            for(int k=1;k<=m;k++)
            {
    
    
                
                if(maps[j][k]<maps[j][i]+maps[i][k]);//通过该点松弛后距离变小了
                maps[j][k]=maps[j][i]+maps[i][k];
                
            
            }
                
    
    
    cout<<maps[][]<<endl;//求得任意2点间的最短路
}

시간 복잡도 (o (m ^ 3))
는이 알고리즘이 질문의 90 %를 통과 할 수 없다고 말할 수 있습니다.


2. Dijkstra 알고리즘
이 알고리즘은 여전히 ​​더 일반적으로 사용되며 마스터해야합니다.

* 핵심 아이디어 :
"가장자리"를 통해 소스 점의 정점에서 나머지 정점까지의 거리를 완화합니다 .

* 특정 단계 :
여기에 사진 설명 삽입
s가
여기에 사진 설명 삽입
s 컬렉션에서 PQ 컬렉션
여기에 사진 설명 삽입
까지 가장 짧은 가장자리찾는 시작 지점이라고 가정 합니다. 지점 2까지의 거리가 가장 짧고 지점
여기에 사진 설명 삽입
6 까지의 거리가 가장 짧고
여기에 사진 설명 삽입
지점 7
여기에 사진 설명 삽입
여기에 사진 설명 삽입여기에 사진 설명 삽입여기에 사진 설명 삽입여기에 사진 설명 삽입
여기에 사진 설명 삽입
(그림 선배가 찍었습니다)

이렇게하면 s 지점에서 모든 지점까지의 최단 경로가 발견됩니다. 코드를 작성하는 방법을 살펴 보겠습니다. (핵심 아이디어를 기억하십시오. "가장자리"를 사용하여 소스 정점에서 나머지 정점까지의 거리를 완화하십시오)

#include<cstdio>
#include<queue>
#include<vector>
#include<iostream>
#define inf 0x3f3f3f
const int maxn=1005;
using namespace std;

typedef pair<int,int> P; //前面放到 dis[i],后面放i 

struct node{
    
    
	int v,w;
}E;
vector<node> edge[maxn];
int dis[maxn];

int main(){
    
    
	int n,m;
	int u;
	while(scanf("%d%d",&n,&m),n){
    
    
		//初始化
		for(int i=1;i<=n;i++){
    
    
			dis[i]=inf;
		} 
		//存图 
		for(int i=1;i<=m;i++){
    
    
			scanf("%d%d%d",&u,&E.v,&E.w);
			edge[u].push_back(E);
		}
		
		priority_queue <P,vector<P>,greater<P> > que;
		while(!que.empty()) que.pop();
		dis[1]=0;
		que.push(P(dis[1],1));
		while(!que.empty()){
    
    
			P now=que.top(); que.pop();
			
			int nowu=now.second;
			for(int i=0;i<edge[nowu].size();i++){
    
    
				E=edge[nowu][i]; 
				if(dis[E.v]>dis[nowu]+E.w){
    
    
					dis[E.v]=dis[nowu]+E.w;
					que.push(P(dis[E.v],E.v));
				}
			}
		}
		for(int i=1;i<=n;i++)
	    	printf("%d ",dis[i]);
	    printf("\n");
	}
	return 0;
} 

/*
6 9
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4
*/

여기서 제가 쓴 것은 우선 순위 대기열 최적화 버전의 시간 복잡도가 O ((m + n) logn)이지만이 알고리즘에는 몇 가지 단점이 있습니다. 음의 가중치 에지로 문제를 해결할 수 없습니다. 왜 루프가 있다고 가정합니다. , 가중치의 합이 음수이면이 루프를 반복하면 비용이 음의 무한대에 접근 할 수 있습니다.


3. Bellman-Ford (단일 소스 측의 최단 경로를 마이너스 가중치로 해결) (측면이 방향성 임)

핵심 아이디어 :
모든 모서리에서 n-1 이완 작업을 수행합니다. k 번째 Bellmam-Ford 이완 작업은 실제로 "최대 k 개의 가장자리를 통해"소스 지점에서 나머지 정점까지의 최단 경로입니다. 따라서 기껏해야 n-1 이완 작업이 수행됩니다.

기본 단계 :
n-1 번의 이완에서 시작점에서 다른 지점까지의 거리를 줄이기 위해 모든 모서리를 반복적으로 횡단합니다.
여기에 사진 설명 삽입
여기에 사진 설명 삽입
여기에 사진 설명 삽입여기에 사진 설명 삽입
여기에 사진 설명 삽입
최적화 :
실제 작업에서는 일반적으로n-1 번의 이완에도달하기 전에 최단 경로가 계산됩니다. 체크를 사용하여 특정 작업 라운드 동안 디스플레이 배열이 변경되었는지 여부를 표시 할 수 있습니다. 변경 사항이 없으면 루프에서 뛰어 내립니다.

#include<cstdio>
#include<vector> 
#include<iostream>
#define inf 0x3f3f3f
const int maxn=1005;
using namespace std;

int dis[maxn];
int u[maxn],v[maxn],w[maxn];

int main(){
    
    
	int n,m,check;
	while(scanf("%d%d",&n,&m),n,m){
    
    
		//存图 
		for(int i=1;i<=m;i++)
			scanf("%d%d%d",&u[i],&v[i],&w[i]);
		
		//初始化dis数组
		for(int i=1;i<=n;i++)
		   dis[i]=inf;
		dis[1]=0;
		
		//进行n-1轮松弛 
		for(int k=1;k<=n-1;k++){
    
    
			check=0;
			//遍历每一条边
			for(int i=1;i<=m;i++){
    
    
				if(dis[v[i]]>dis[u[i]]+w[i]){
    
    
					dis[v[i]]=dis[u[i]]+w[i];
					check=1;				
				}
			}                 
			if(check==0) break;
		}
		
		for(int i=1;i<=n;i++)
		    printf("%d ",dis[i]); 
		printf("\n");
		
		/*
		//判断是否有负权回路 
		int flag=0;
		for(int i=1;i<=m;i++)
		    if(dis[v[i]]>dis[u[i]]+w[i]) flag=1;
		if(flag==0) printf("no\n");
		else printf("yes\n");
		*/
	}
	return 0;
}
/*
5 5
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
*/

(선배 자매 사진 감사합니다)

추천

출처blog.csdn.net/jahup/article/details/106982608