최 단 로 문제 DijKstra 알고리즘 Bellman - ford 와 spfa 알고리즘
9248 단어 알고리즘 입문
준비 단계
int n,m,s,e;
int mp[210][210];
int d[210];
int vis[210];
n, m 는 n 개의 점, m 개의 변, s 는 시작 점 (중앙 노드 는 d [Max] 배열 과 관련) 이 고 가장 짧 은 길 을 원 하 는 점 은 e 이다.mp [i] [j] 2 차원 배열 은 i 점 에서 j 점 까지 의 거 리 를 저장 합 니 다.
입력 및 삭제 단계
while(scanf("%d %d",&n,&m) == 2)
{
getchar();
memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d));
for(int i = 0;i < 210;i++)
{
for(int j = 0;j < 210;j++)
{
if(i == j)mp[i][j] = 0;
else mp[i][j] = mp[j][i] = inf;
}
}
for(int i = 0;i < m;i++)
{
scanf("%d %d %d",&a,&b,&x);
getchar();
if(mp[a][b] > x)mp[a][b] = mp[b][a] = x;// ,
}
scanf("%d %d",&s,&e);
getchar();
Dijkstra(s,e);
}
mp 배열 을 업데이트 합 니 다. mp [i] [i] 할당 이 0 이 아 닌 나머지 할당 값 은 inf (비교적 큰 값) 입 니 다. 이것 은 알 수 없 는 4 거 리 를 표시 하고 후속 입력 업데이트 mp 를 편리 하 게 하기 위해 서 입 니 다.업 데 이 트 를 입력 할 때 는 두 가지 다 중 상황 을 조심해 야 하 며, 마지막 으로 가장 짧 은 길 만 기록 하면 된다.
최 단 로 dijkscar 알고리즘 찾기
int next;
// s
for(int i = 0;i < 210;i++)
{
d[i] = mp[s][i];
}
vis[s] = 1;
for(int i = 0;i < 210;i++)
{
//
int minnum = inf;
for(int j = 0;j < 210;j++)
{
if(!vis[j] && d[j] < minnum)
{
minnum = d[j];
next = j;
}
}
if(minnum == inf)
{
break;
}
vis[next] = 1;
//
for(int j = 0;j < 210;j++)
{
if(!vis[j] && d[j] > mp[next][j] + d[next])//
{
d[j] = mp[next][j] + d[next];
}
}
}
if(d[e] != inf)
printf("%d
",d[e]);
else
printf("-1
");
먼저 중앙 노드 s 가 연 결 된 가장자리 에 따라 1 파 d [i] 배열 을 업데이트 합 니 다. 그러면 한 개의 점 과 한 개의 선 이 생 겼 습 니 다. 그 다음 에 이 라인 에서 가장 짧 은 것 을 찾 은 다음 에 걸 어 갑 니 다. (vis 배열 업데이트) 그러면 두 개의 점 과 한 개의 선 이 이미 지 났 습 니 다. (강력 히 제안 합 니 다) 방금 찾 은 점 에 따라 연 결 된 변 에 따라 1 파 d [i] 를 업데이트 합 니 다.배열 (여기 서 전문 적 으로 이완 이 라 고 함) 은 모든 선 에 대해 판단 해 야 할 것 은 다음 에 연 결 된 점 이 방문 한 적 이 있 는 지, 이 선의 시작 (s 중앙 노드) 부터 끝 (첫 번 째 다음 에 연 결 된 점) 까지 의 거리 가 현재 d [] 배열 에 저 장 된 것 이 작 아야 업데이트 할 필요 가 있 잖 아 요. (이것 도 처음에 mp 2 차원 배열 이 inf 로 초기 화 된 이유 입 니 다)
찾 을 수 없 을 때 까지 이렇게.
그러나 dijkscar 알고리즘 에 치 명 적 인 약점 이 있 습 니 다. 즉, 마이너스 회로 가 있 는 상황 이 발생 하면 vis 배열 로 인해 구 하 는 최 단 로 오 류 를 초래 할 수 있 습 니 다. 마이너스 회로 가 발생 하면 최 단 로 가 없 기 때 문 입 니 다.
여기 서 특히 주의해 야 한다. 평소에 우리 가 고려 해 야 할 것 은 모두 양 방향 변 이기 때문에 네가 입력 한 변 값 이 마이너스 라면 이미 마이너스 회로 이다.
여 기 는 hdu 2544 고전 템 플 릿 문 제 를 예 로 들 면
일단 준비 단 계 를 진행 하도록 하 겠 습 니 다.
int N,M;
struct edge
{
int from;
int to;
int cost;
}e[10010];
int d[110];
void init()
{
memset(e,0,sizeof(e));
}
이런 의 미 를 다 알 아 볼 수 있다 고 믿 습 니 다. bijkscar 알고리즘 과 차이 가 많 지 않 습 니 다.다음은 입력 과 비우 기 단계 입 니 다.
while(scanf("%d %d",&N,&M) == 2)
{
getchar();
if(!N && !M)break;
init();
for(int i = 1;i <= M * 2;i ++)
{
scanf("%d%d%d",&e[i].from,&e[i].to,&e[i].cost);
i++;
e[i].from = e[i - 1].to;//
e[i].to = e[i - 1].from;
e[i].cost = e[i - 1].cost;
getchar();
}
if(Bellman(1,N))
{
printf("%d
",d[N]);
}
else
{
cout<
양 방향 변 의 입력 문 제 를 주의 하 시 면 됩 니 다.다음은 Bellman - ford 알고리즘 입 니 다.
for(int i = 1;i <= N;i++)
{
if(i != s)d[i] = inf;
else d[i] = 0;
}
첫 번 째 독립 된 for 순환 은 d [s] 만 0 으로 업 데 이 트 됩 니 다.for(int i = 2;i <= N;i++)
{
for(int j = 1; j <= M * 2;j++)
{
if(d[e[j].to] > d[e[j].from] + e[j].cost)
{
d[e[j].to] = d[e[j].from] + e[j].cost;
}
}
}
그리고 두 번 째 독립 된 for 순환i. 업데이트 횟수 를 제어 합 니 다. 매번 업데이트 할 때마다 한 점 의 최 단 로 를 확인 할 수 있 기 때 문 입 니 다. n 점 은 n 번 업데이트 가 필요 합 니 다.
j 가 제어 하 는 것 은 모든 변 을 옮 겨 다 니 는 것 이다.
내 려 온 이완 (최적화) 에 대한 판단 사상 은 dijkscar 와 유사 하 다.
int flag = 1;
for(int j = 1; j <= M * 2;j++)
{
if( d[e[j].to] > d[e[j].from] + e[j].cost )
{
flag = 0;
break;
}
}
return flag;
다음은 마이너스 회로 의 판단 이다.네가 다시 한 번 업 데 이 트 를 진행 해라. 만약 업 데 이 트 를 할 수 있다 면 틀림없이 마이너스 회로 가 존재 할 것 이다
사실 당신 은 발견 할 수 있 습 니 다. 이 for 순환 은 분명히 이전 for 순환 에서 떼 어 낸 작은 부분 이 잖 아 요. 위 에 for 순환 의 i 초기 값 을 1 로 설정 하고 내부 에 판단 을 추가 하면 여 기 는 절약 할 수 있 고 이해 하기 편리 합 니 다 ~
여전히 2544 문제 입 니 다. 다음은 spfa 알고리즘 입 니 다. 가장 많이 사용 하 는 알고리즘 입 니 다.
spfa 는 점 을 겨냥 하여 조작 하 는 것 입 니 다.
점 을 겨냥 한 알고리즘 에 대해 우 리 는 두 가지 사고방식 으로 나 눌 수 있다. 첫 번 째 는 체인 식 전방 향 성 이 고, 두 번 째 는 2 차원 구조 체 동적 수조 이다.
먼저 체인 식 전방 향 성의 응용 을 되 돌아 보 겠 습 니 다.
준비 단계
using namespace std;
struct edge{
//int from;
int to;
int cost;
int reid;
}e[20010];
int N,M,cnt;
int vis[110];
int id[110];
int time[110];
int d[110];
reid 는 이 점 에 포 함 된 변 앞 뒤 를 연결 하 는 변수 입 니 다.time 배열 은 마이너스 회로 가 있 는 지 없 는 지 를 판단 하 는 데 쓰 인 다.
입력 및 삭제
while(scanf("%d %d",&N,&M) != EOF)
{
getchar();
if(!N && !M)break;
init();
for(int i = 1;i <= M;i++)
{
int a,b,x;
scanf("%d%d%d",&a,&b,&x);
getchar();
add(a,b,x);// ,
add(b,a,x);
void add(int from,int to,int cost)
{
e[cnt].to = to;
e[cnt].cost = cost;
e[cnt].reid = id[from];
id[from] = cnt;
cnt++;
}
void init()
{
for(int i = 0;i < 110;i++)
{
d[i] = inf;
id[i] = -1;
}
cnt = 0;
memset(e,0,sizeof(e));
memset(vis,0,sizeof(vis));
memset(time,0,sizeof(time));
}
init () 함수 의 초기 화 를 살 펴 보 겠 습 니 다. d [i] 배열 은 최대 값 으로 초기 화 되 었 습 니 다. id [i] 배열 은 - 1 로 초기 화 되 었 습 니 다. 대표 적 인 의 미 는 i 점 이 연결 되 어 있 는 아래 표 시 는 - 1 입 니 다. 연 결 된 아래 표 시 를 알 수 없습니다. cnct 는 0 으로 초기 화 되 었 습 니 다. 모든 변 수 는 아래 표 시 된 변수 입 니 다.그리고 spfa 알고리즘
queue q;
q.push(S);
vis[S] = 1;
d[S] = 0;
int flag = 0;
먼저 중앙 노드 push 를 넣 고 이 점 을 찾 았 는 지 판단 한다. flag 는 마이너스 회로 가 있 는 지 없 는 지 판단 한다.while(q.size())
{
int now = q.front();//q , :?
q.pop();
for(int i = id[now];~i;i = e[i].reid)
{
if(d[e[i].to] > d[now] + e[i].cost)
{
d[e[i].to] = d[now] + e[i].cost;
if(!vis[e[i].to])
{
vis[e[i].to] = 1;
time[e[i].to]++;
if(time[e[i].to] > N)
{
flag = 1;
break;
}
q.push(e[i].to);
}
}
}
if(flag)break;
vis[now] = 0;// , , !
}
튕 겨 나 온 첫 번 째 점 입 니 다. 첫 번 째 점 에 연 결 된 모든 변 을 옮 겨 다 니 며 d [i] 배열 을 업데이트 한 다음 에 연 결 된 점 을 관찰 하고 방문 상 태 를 업데이트 하 며 방문 횟수 를 업데이트 합 니 다.만약 에 한 점 의 방문 횟수 가 N 보다 많 으 면 마이너스 회로 가 존재 한 다 는 뜻 이다. 원리 와 Bellman - ford 알고리즘 이 차이 가 많 지 않 고 마지막 으로 주의해 야 할 점 은 vis [i] 이다.
그 상 태 를 0 으로 다시 갱신 하 는 것 도 마이너스 회 로 를 판단 하기 위 한 것 이다.
quue 는 우선 순위 가 있 을 수 없습니다. 주어진 순서에 따라 중앙 노드 bfs 에서 밖으로 방문 하 는 것 입 니 다. 일단 흐 트 러 지면 d [i] 가 업데이트 되 지 않 았 습 니 다. 당신 의 다음 단계 입 니 다.
업데이트 가 진행 되 었 습 니 다.
다음은 대열 의 실현 이다.
준비 작업 을 보다
int dis[maxn];
int v,e;// ,
queue q;
int flag[maxn];//
int times[maxn];//
//
struct graph
{
int s,e,w;
}es[maxn];
입력 한 것 은 생략 했 습 니 다.다음은 spfa 로 들 어 갑 니 다.
for(i=1;i<=v;i++)
{
dis[i]=info;
flag[i]=0;
times[i]=0;
}
dis[s]=0;
q.push(s);
times[s]++;
한 파 를 초기 화한 다음 에 중앙 노드 에 대해 한 파 를 초기 화 합 니 다.while(!q.empty())
{
int t = q.front();
q.pop();
flag[t]=0;
for(i=1;i<=e;i++)
{
if(dis[es[i].e]>dis[es[i].s]+es[i].w)
{
dis[es[i].e]=dis[es[i].s]+es[i].w;
if(flag[es[i].e]==0)
{
q.push(es[i].e);
flag[es[i].e]=1;
times[es[i].e]++;
if(times[es[i].e]==v)
return false;// // !!
}
}
}
}
대열 의 첫 번 째 점 을 꺼 내 면 이곳 의 flag 는 vis 입 니 다.그 다음 에 모든 변 을 최적화 시 키 고 차이 가 많 지 않 은 원 리 는 여기 서 설명 하지 않 겠 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
인접하지 않는 문제-상압 Dp-HDU1565,POJ3254먼저 만든 HDU 1565 격자 수 문제는 격자 수에서 얻은 수를 서로 인접할 수 없으며 최대 얻을 수 있는 총계가 얼마냐고 물었다 쉽게 생각할 수 있다--dp[i][j]는 현재 i행 j상태에서 얻은 수를 나타낸다....
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.