최단 경로 (DIJ)-포지티브 및 네거티브 매핑 / 메일 배달

  저는 오늘이 질문을했습니다 : 최단 경로를
  검색 한 결과 이것이 실제로 고전적인 그래프 이론 문제라는 것을 발견했습니다. 우편 배달부가 편지를 보냅니다. 둘의 차이점은이 질문이 여러 번이고 우편 배달부가 편지를 보낸다는 것입니다. 한번. 따라서 q 변수가 코드의 주 함수에 추가됩니다.
  이 질문의 특징은 장소에 도착할 때마다 출발점으로 돌아가 다시 시작해야한다는 것입니다. 최단 경로 문제로 분류하기 쉽습니다. 이 질문의 실현 아이디어 는 긍정적이고 부정적인 그림구축하는 것 입니다.

  알고리즘 아이디어 : 모든 노드를 하나씩 분할 할 수 있습니다. 앞뒤로 가면서가는 과정은 시작점 u에서 v까지의 최단 경로로 이해 될 수 있습니다. 즉, 순방향 매핑입니다. 돌아 오는 과정에서 모든 모서리를 뒤집은 다음 u에서 v까지의 최단 경로를 계산합니다 (사실이 아이디어는 특별히 이해가 안 돼요 ~).

  나는 인터넷에서 많은 코드를 발견했고 다른 사람들의 글쓰기에 문제가 없으며 완전히 AC가 가능합니다. 하지만 나 같은 초보자에게는 그다지 친절하지 않습니다. 통일 된 기능이 있습니다 : 코멘트가 없습니다. 그래서 이해하는데 오랜 시간이 걸렸고 마침내 각 레이어의 의미를 이해했습니다.

  그래프를 구성 할 때 주로 인접 목록의 방식입니다. 구조면과 결합 된 1 차원 배열 헤드를 사용하여 표현합니다. 앞면과 뒷면에 두 장의 사진이 있으며 많은 자료가 두 장을 구분합니다. 그러나 2 차원 형태를 찾는 것은 비교적 간단합니다. 0은 정방향을 의미하고 1은 역방향을 의미합니다. 최단 경로 방법은 DJ Tesla로, 우선 순위 대기열 (이미 정렬, 먼저 정렬 됨)을 사용하여 구현됩니다.

  소스 코드는 다음과 같습니다.

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mamx = 1e5+5;
// 之前有人把这个结构体定义为node,但其实里面信息都是有关于边的 
struct side{
    
     
	// v代表该边的指向
	/*
	 *  next代表相同开始边u连接的其他边,使用是邻接表的结构。但其实这个next很有歧义。
	 * 在Addedge中,next连接的是相同开始边u的不同v,但拼接的时候其实是一个反向更新 
	*/ 
	int v, next;  
	ll w; // w代表该边的权重 
}a[2][mamx]; // a代表的是边 

ll num1, num2; // num1代表正向遍历当前的变数,num2代表反向遍历当前的边数 
ll dis[2][mamx]; // dis代表各点据起始点的最短距离 
ll head[2][mamx]; // head[][i]代表以i为起点,最新的边序号 
bool visit[mamx]; // visit代表该节点是否已经被访问 
int n, m;

void Addedge(int i, int u, int v, int w) {
    
    
	int num;
	if (i == 0)
		num = num1;
	else
		num = num2;
	// num通过先自增,这样num代表的是最新的边序号 
	a[i][++num].next = head[i][u]; // 采用倒序方法,当前边的next是上一条的编号 
	head[i][u] = num; // head[i][u]代表以u为起点的最新的边的编号 
	a[i][num].v = v;
	a[i][num].w = w;
	// 更新正向/反向当前的边数 
	if (i == 0)
		num1 = num;
	else
		num2 = num;
}
/*
 *	迪杰特斯拉的实现使用了优先队列,基本还是按照迪杰特斯拉的方法。
 *	区别在于使用优先队列,免除了不断寻找最小值的的过程。 
*/ 
void dij(int x) {
    
    
	for (int i=1; i<=n; i++) {
    
    
		dis[x][i] = 1e16; // 寻找最小值,传入的值应该尽可能大 
		visit[i] = 0;
	}
	priority_queue<pair<ll, int> > q; // ll类型代表权值,int类型是指向 
	dis[x][1] = 0;
	q.push(make_pair(0,1));
	while (!q.empty()) {
    
    
		int u = q.top().second;
		q.pop();
		if (visit[u] == 1)
			continue;
		visit[u] = 1;
		for (int i=head[x][u]; i; i=a[x][i].next) {
    
    
			int v = a[x][i].v;
			ll w = a[x][i].w;
			if (dis[x][v] > dis[x][u] + w) {
    
    
				dis[x][v] = dis[x][u] + w;
				/* 
					此处传入的是负值,原因在于:我们希望是递增序列,这样从左到右遍历寻找
					到的第一个visit[u]为false的就是当前最小的。但优先级队列默认是降序排列
					此处加入负号,达到递增目的。 
				*/ 
				q.push(make_pair(-dis[x][v], v));
			}
		}
	}
} 
int main(void) {
    
    
	int q;
	cin >> q; 
	while (q--) {
    
    
		memset(head,0,sizeof(head));
		cin >> n >> m;
		num1 = 0;
		num2 = 0;
		for (int i=1; i<=m; i++) {
    
    
			int u, v, w;
			cin >> u >> v >> w;
			Addedge(0,u,v,w);
			Addedge(1,v,u,w);
		}
		ll ans = 0;
		dij(0); // 0代表正向 
		dij(1); // 1代表反向
		for (int i=2; i<=n; i++) {
    
     // 自身不计算,从第二个点开始 
			ans += dis[0][i] + dis[1][i]; 
		} 
		cout << ans << endl;
	}
	return 0;
}

참조 자료 :
https://blog.csdn.net/JiangHxin/article/details/104059688
https://blog.csdn.net/weixin_36888577/article/details/79937886

추천

출처blog.csdn.net/gls_nuaa/article/details/114856080