최근에 최단 경로를 검토하고 내 인상을 강화하는 방법으로 블로그를 씁니다.
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
*/
(선배 자매 사진 감사합니다)