[TJOI2019]大中锋的游乐场 题解

题目传送门~

题目描述

一个人在一个无向图内,点权为 \(1\)\(-1\),求在任意时刻下,经过点权和绝对值不超过 \(k\) 的最短路。

\(1≤n≤10^4,1 \leq m\leq 10^5,1 \leq k\leq 10\)

想法

根据数据范围中 \(1\leq k \leq 10\) 不难想到这是一道分层图题。

思路

分层的依据就照着题目设好了:以经过点权和为 \(j(-k\leq j\leq k)\) 进行分层即可。

对于边,我们进行如下操作:

  • 一条边 \((u, v, w)\),其中 \(u\)\(i\) 层,\(v\)\(i + type[v]\) 层,连接一条 \(u\)\(v\),权重为 \(w\) 的边。

\(type[v]\) 表示 \(v\) 的点权。

  • 注意由于到达一个点就一定会改变点权和,因此对于这道题而言没有层间的边。

实现

为了方便这题我用逻辑建图的方法,不实际把图建起来,而是在进行算法过程的时候有条件地考虑可行点。

  • 数组下标不能是负数,所以把所有的表示层数的 \(j\) 加上 \(k\),这样 \(j\) 的取值范围就变为了: \(0\leq j\leq 2k\),方便很多。

  • Trick: 由于第一个点也需要考虑,所以干脆直接从 \(0\) 连了一条向起点 \(S\)\(0\) 边,最短路的起点就设成 \(0\),这样省去了分类讨论。

// Problem: P5340 [TJOI2019]大中锋的游乐场
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P5340
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Author: Moyou
// Copyright (c) 2022 Moyou All rights reserved.
// Date: 2022-12-14 20:20:58

vector<PII> g[N];
int n, m, k, a[N];
int S, T;
bool st[N][15];
int dist[N][15];

void add(int a, int b, int c)
{
    g[a].push_back({b, c});
}

struct qwq
{
    int id, type, d;
    bool operator<(const qwq &D) const
    {
        return d > D.d;
    }
};

void init()
{
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i++)
        g[i].clear();
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        if (a[i] == 2)
            a[i] = -1;
    }
    memset(st, 0, sizeof st);
    memset(dist, 0x3f, sizeof dist);
    for (int i = 1; i <= m; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c), add(b, a, c);
    }
    cin >> S >> T;
    add(0, S, 0);
}

int dijkstra()
{
    priority_queue<qwq> heap;
    heap.push({0, k, 0});
    dist[0][k] = 0;
    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();
        int u = t.id, ty = t.type;
        if (st[t.id][t.type])
            continue;
        st[u][ty] = 1;
        for (auto i : g[u])
        {
            int w = i.y, j = i.x;
            if (a[j] + ty <= 2 * k && a[j] + ty >= 0 && dist[j][a[j] + ty] > dist[u][ty] + w)
            {
                dist[j][a[j] + ty] = dist[u][ty] + w;
                heap.push({j, a[j] + ty, dist[j][a[j] + ty]});
            }
        }
    }
    int ans = INF;
    for (int i = 0; i <= 2 * k; i++) // 取最小答案
    {
        ans = min(ans, dist[T][i]);
    }
    return (ans == INF ? -1 : ans);
}

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        init();
        cout << dijkstra() << endl;
    }

    return 0;
}

posted @ 2022-12-14 21:18  MoyouSayuki  阅读(18)  评论(0编辑  收藏  举报
:name :name