Dijkstra序列(Dijkstra算法)
题意
给定一个\(N\)个点\(M\)条边的无向连通图,没用重边和自环。
在Dijkstra算法中,我们需要不断维护一个包含最短路径树中顶点的集合。
在每一步中,我们找到一个尚未在集合内且与源顶点距离最小的顶点,并将其收于集合中。
因此,通过Dijkstra算法,我们可以逐步生成一个有序的顶点序列,我们称之为Dijkstra序列。
现在有\(Q\)个操作,每次操作给定一个长度为\(N\)的节点序列,问该节点序列是否是Dijkstra序列。
题目链接:https://www.acwing.com/problem/content/4278/
数据范围
\(1 \leq N \leq 1000\)
\(1 \leq M \leq 10^5\)
\(1 \leq Q \leq 100\)
思路
在Dijkstra算法中,我们每次选取距离最近的点加入点集。
因此,我们将序列的第一点作为起点,跑Dijkstra算法。
遍历每个点,如果当前点不是集合外距离最近的点,那么序列不合法;然后对其他点的距离进行更新。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m, q;
int g[N][N];
int dist[N];
bool st[N];
int a[N];
bool dijkstra(int u)
{
memset(dist, 0x3f, sizeof dist);
memset(st, 0, sizeof st);
dist[u] = 0;
for(int i = 1; i <= n; i ++) {
int t = a[i];
for(int j = 1; j <= n; j ++) {
if(!st[j] && dist[t] > dist[j]) {
return false;
}
}
for(int j = 1; j <= n; j ++) {
dist[j] = min(dist[j], dist[t] + g[t][j]);
}
st[t] = true;
}
return true;
}
int main()
{
scanf("%d%d", &n, &m);
memset(g, 0x3f, sizeof g);
for(int i = 0; i < m; i ++) {
int x, y, d;
scanf("%d%d%d", &x, &y, &d);
g[x][y] = d, g[y][x] = d;
}
scanf("%d", &q);
while(q --) {
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
if(dijkstra(a[1])) puts("Yes");
else puts("No");
}
return 0;
}