洛谷-P14331 [JOI2021 预选赛 R2] 煎饼 / Pancake

洛谷-P14331 [JOI2021 预选赛 R2] 煎饼 / Pancake

P14331 [JOI2021 预选赛 R2] 煎饼 / Pancake - 洛谷

用 \(N\) 张煎饼堆叠成煎饼塔,煎饼共有三种口味,分别称为 A、B、C。

将满足以下条件的煎饼塔称为“良好煎饼塔”:

  • 对于任意一张口味 A 的煎饼与一张口味 B 的煎饼,口味 A 的煎饼必须位于口味 B 的煎饼之上。
  • 对于任意一张口味 A 的煎饼与一张口味 C 的煎饼,口味 A 的煎饼必须位于口味 C 的煎饼之上。
  • 对于任意一张口味 B 的煎饼与一张口味 C 的煎饼,口味 B 的煎饼必须位于口味 C 的煎饼之上。

可以对煎饼塔执行以下操作:

  • 操作 \(k\):反转从顶部数起前 \(k\) 张煎饼的排列顺序。

现共有 \(Q\) 座煎饼塔,第 \(i\) 座煎饼塔(\(1 \le i \le Q\))从上至下口味序列为 \(S_{i,1} S_{i,2} \cdots S_{i,N}\)。

比太郎希望对每一座煎饼塔,用尽可能少的操作次数将其变为良好煎饼塔。

给定 \(Q\) 座煎饼塔的口味排列信息,请编写程序,求出每座煎饼塔变为良好煎饼塔所需的最少操作次数。

\(2 \le N \le 13\),\(1 \le Q \le 100\,000\)。

三进制状态压缩。将每一种口味的排列映射为一个三进制数,进而用一个节点表示。

如果对一种状态进行一次操作,可以转换为另一种状态,那么就在这两个状态对应的节点之间连边。

那么,所要求的就是从给定状态到任意“良好状态”的最短路径。

由于目标良好状态是已知的,而起点有 \(Q\) 个是不固定的,因此我们不妨反过来,预处理出从任意目标状态变成任意其他状态所需的最少次数(在图中即是经过的最少边数)。这样,不论询问以哪一个状态为起点,都可以 \(O(1)\) 回答。

边权全为 \(1\),故采用多源 BFS 进行预处理。时间复杂度 \(O(N\cdot3^N+Q)\)。

(套路类似的题:Gym-105578 Light Up the Grid。)

#include <bits/stdc++.h>
using namespace std;
int const N = 1.6e6;
int n, Q, dis[N];

int to_ternary(string s) {
    int res = 0;
    for (char c: s)
        res = res * 3 + (c - 'A');
    return res;
}

void from_ternary(int x, string &s) {
    s.resize(n);
    for (int i = n - 1; i >= 0; i--) {
        s[i] = (x % 3) + 'A';
        x /= 3;
    }
}

int main() {
    cin.tie(0)->sync_with_stdio(0);

    cin >> n >> Q;
    memset(dis, -1, sizeof dis);
    queue<int> q;
    for (int i = 0; i <= n; i++) {
        for (int j = 0; i + j <= n; j++) {
            int k = n - i - j;
            string s;
            for (int x = 0; x < i; x++) s += 'A';
            for (int x = 0; x < j; x++) s += 'B';
            for (int x = 0; x < k; x++) s += 'C';
            int u = to_ternary(s);
            dis[u] = 0;
            q.push(u);
        }
    }

    while (!q.empty()) {
        int u = q.front();
        q.pop();
        string s;
        from_ternary(u, s);
        for (int k = 2; k <= n; k++) {
            reverse(s.begin(), s.begin() + k);
            int v = to_ternary(s);
            if (dis[v] == -1) {
                dis[v] = dis[u] + 1;
                q.push(v);
            }
            reverse(s.begin(), s.begin() + k);
        }
    }

    while (Q--) {
        string s;
        cin >> s;
        int u = to_ternary(s);
        if (dis[u] == -1) cout << "-1\n";
        else cout << dis[u] << '\n';
    }

    return 0;
}
posted @ 2026-05-10 18:30  f2021ljh  阅读(21)  评论(0)    收藏  举报