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;
}
posted @ 2022-06-17 15:09  pbc的成长之路  阅读(118)  评论(0编辑  收藏  举报