저는 오늘이 질문을했습니다 : 최단 경로를
검색 한 결과 이것이 실제로 고전적인 그래프 이론 문제라는 것을 발견했습니다. 우편 배달부가 편지를 보냅니다. 둘의 차이점은이 질문이 여러 번이고 우편 배달부가 편지를 보낸다는 것입니다. 한번. 따라서 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