spfa 알고리즘
spfa 알고리즘은 대기열 최적화의 Bellman-Ford 알고리즘입니다.
벨만 - 포드 알고리즘에서, 완화 기능에있어서 dis[b]=min(dis[b],dis[a]+w)
, M의 에지는 N-1 번을 스캔마다 스캔되고, 시간 복잡도는 O (MN).되는
각각의 에지에 여러 번 사용될 수 있기 때문에,이를 기반으로 할 수 없다 사용 횟수가 최적화됩니다. 따라서 최적화 아이디어를 변경합니다. 완화 함수에 따르면 dis[b]=min(dis[b],dis[a]+w)
dis [b]를 수정할 때마다 dis [a]가 변경 되었기 때문이어야합니다. dis [a] 때문에 수정은 dis [b]의 수정만을 포함합니다.
따라서 유지 보수를 위해 큐를 사용하고, 시작점부터 수정 된 각 포인트 a를 큐에 넣고 큐의 헤드를 차례로 꺼내 연결된 정점을 수정합니다.
따라서 Bellman-ford 알고리즘과 달리 spfa 알고리즘에서는 꼭짓점을 기반으로 연결된 모든 모서리를 찾아야하기 때문에 각 모서리의 정보뿐만 아니라 그래프의 저장 구조를 알아야합니다. .出度
Spfa 알고리즘 단계 :
대기열 <— 시작점 s
대기열이 비어 있지 않은 경우
(1) t <— 대기열 헤드
queue.pop ()
(2) 모든 나가는 에지 t –> b를 t로 업데이트하고 가중치는 w
대기열 <– b입니다. 포인트는 업데이트입니다.이 포인트를 사용하여 다른 포인트를 업데이트합니다.)
큐에서 버텍스가 중복되는 현상을 피하기 위해 bool 배열을 사용하여 유지 관리합니다. 참이면 업데이트 대기중인 큐에 추가되었으며 다시 큐에 참가할 필요가 없음을 의미합니다. .
시간 복잡도 일반 : O (m) 최악 : O (nm)
제목 설명
n 개의 점과 m 개의 간선이있는 유 방향 그래프가 주어지면 그래프에 여러 간선과 자체 루프가있을 수 있으며 간선 가중치는 음수 일 수 있습니다.
1 번 지점에서 n 번 지점까지의 최단 거리를 찾으세요. 1 번 지점에서 n 지점까지 걸을 수 없으면 출력이 불가능합니다. ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ ㅇㅇㅇ
데이터 보증에는 음의 가중치 루프가 없습니다.
입력 형식 첫
번째 줄에는 정수 n과 m이 포함됩니다.
다음 m 개 행 각각에는 3 개의 정수 x, y, z가 포함되어 있으며, 이는 점 x에서 점 y로 향하는 가장자리가 있고 변의 길이가 z임을 나타냅니다.
출력 형식
1 점에서 n 점까지의 최단 거리를 나타내는 정수를 출력합니다.
경로가 존재하지 않으면 "불가능"을 출력합니다.
데이터 범위는
1≤n, m≤10 5 이며
그림에서 측면 길이 의 절대 값은 10000을 초과하지 않습니다.
입력 샘플 :
3 3
1 2 5
2 3 -3
1 3 4
샘플 출력 :
2
무거운 모서리 및 자체 루프의 영향을 분석 합니다.
- 무거운 가장자리는 가장 작은 무게의 가장자리를 취하는 한 효과가 없으며 여러 번 반복하는 것입니다.
- 자체 루프도 루프이지만 제목에는 음의 가중치 루프가 없으므로 양의 가중치 루프의 영향을 무시할 수 있습니다. 가중치가 클수록 가중치가 더 크므로 선택하지 않습니다. 이 경로.
알고리즘 구현
#include <iostream>
#include <cstring>
using namespace std;
const int N=1e5+10,M=1e5+10,INF=0x3f3f3f3f;
int h[N],e[M],w[M],ne[M],idx;
int dis[N];
int q[N],hh,tt=-1;//手写队列
bool vis[N]; //判断顶点是否已经加入队列
int n,m;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void spfa(int s)
{
memset(dis,0x3f,sizeof dis);
dis[s]=0;
q[++tt]=s,vis[s]=true; //默认第一个距离已经被修改,加入队列
//vis数组进行优化,当队列中已有该结点时,就不用再插入了,即上次这次合并起来一起更新
while (hh<=tt) {
int v=q[hh++];
vis[v]=false;
for (int i=h[v];i!=-1;i=ne[i]) {
//更新所有的dis[b]
int j=e[i]; //v->j的边,权值为w[i]
if (dis[j] > dis[v]+w[i]) {
dis[j]=dis[v]+w[i]; 由于是只有v顶点更改后才会更改j顶点的,所以不存在v顶点的dis[v]是INF的情况
if (!vis[j]) q[++tt]=j,vis[j]=true; //也就是不需要像bellman-ford算法那样if(dis[a]!=INF)判断了
}
}
}
}
int main()
{
memset(h,-1,sizeof h);
scanf("%d%d",&n,&m);
int a,b,c;
for (int i=0;i<m;i++) {
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);//有向图:a->b,权值为c
}
spfa(1);//求起点为1号结点到其它所有结点的距离
dis[n]==INF?puts("impossible"):printf("%d",dis[n]);
return 0;
}
Bellman-ford 알고리즘과 비교할 때 spfa 알고리즘은 dis [b]를 수정할 때 dis [a]의 판단이 여전히 부족합니다 if(dis[a]!=INF)
. b 꼭지점은 a 꼭지점이 변경된 후에 만 변경되므로 dis [a]가 없습니다. 꼭지점입니다. INF의 경우입니다.
가장자리의 가중치가 INF (
가능성 없음)이거나 시작점 dis [s]가 초기화되지 않은 경우 INF입니다.
spfa 알고리즘은 Bellman-ford 알고리즘의 최적화
이지만 특정 경로 길이에서 최소 경로 가중치를 계산할 수 없으며
Bellman-ford 알고리즘에 음의 가중치 루프가 있어도 경로 가중치를 얻을 수 없습니다. 응답하지만 루프를 종료 할 수
있으며 spfa 알고리즘을 사용할 때 음의 가중치 루프가있는 경우 음의 가중치 루프가 경로 i-> j에있을 때 대기열이 비어 있지 않기 때문에 루프를 계속합니다. , 그리고 경로 가장 작은 것은 끝이 극소로 끝납니다.
상황을 상상해보십시오.
자가 루프가 있고 가중치가 음수 일 때 대기열을 대기열의 맨 앞에두고 자신을 자신의 편으로 업데이트 한 후 대기열에 다시 들어가므로 무한 루프가됩니다.
따라서 spfa 알고리즘은 알려진 그래프에 음의 가중치 루프가 없거나 경로 i-> j에 음의 가중치 루프가없는 경우에만 사용할 수 있습니다.