1 条题解

  • 0
    @ 2025-6-16 20:26:50

    解题思路:

    本问题要求通过最少的转置操作(交换两个连续书块)将书籍序列排序成升序1,2,...,n(1,2,...,n)。采用迭代加深的DFSIDADFS(IDA*)算法解决:

    关键步骤:

    启发式函数:估算当前状态到目标状态的最小转置次数

    迭代加深:从00开始逐步增加深度限制0——4(0——4)

    状态转移:枚举所有可能的书块交换操作

    剪枝优化:当当前深度 ++ 估计值 >> 最大深度时剪枝

    C++实现:

    #include <bits/stdc++.h>
    #define ll long long
    #define io cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
    #define ri register int  // 寄存器变量优化
    using namespace std;
    
    const int N = 15;  // 最大书籍数量
    
    int n;
    int a[N], w[5][N];  // a:当前书籍序列, w:保存中间状态
    
    // 启发式函数:估算完成排序所需的最小转置次数
    inline int f() {
        int res = 0;
        // 遍历序列,统计不符合升序的位置数量
        for (ri i = 0; i + 1 < n; i++)
            if (a[i + 1] != a[i] + 1)  // 检查相邻书籍是否连续
                res++;
        // 每轮转置最多修复3个位置,向上取整估算
        return (res + 2) / 3;
    }
    
    // DFS搜索:tmp-当前深度, maxs-最大深度限制
    inline bool dfs(int tmp, int maxs) {
        // 剪枝:当前深度+估计值超过限制
        if (tmp + f() > maxs) return false;
        // 成功条件:序列已完全排序
        if (f() == 0) return true;
        
        // 枚举所有可能的转置操作
        for (int l = 0; l < n; l++)        // 第一书块起始位置
            for (int r = l; r < n; r++)   // 第一书块结束位置
                for (int k = r + 1; k < n; k++) {  // 第二书块结束位置
                    // 保存当前状态
                    memcpy(w[tmp], a, sizeof a);
                    int x, y;
                    
                    // 执行转置操作:
                    // 1. 将第二书块(r+1到k)移到l位置
                    for (x = r + 1, y = l; x <= k; x++, y++)
                        a[y] = w[tmp][x];
                    // 2. 将第一书块(l到r)接到后面
                    for (x = l; x <= r; x++, y++)
                        a[y] = w[tmp][x];
                    
                    // 递归搜索下一层
                    if (dfs(tmp + 1, maxs)) return true;
                    
                    // 回溯:恢复原始状态
                    memcpy(a, w[tmp], sizeof a);
                }
        return false;
    }
    
    int main() {
        io;  // 加速IO
        int t;
        cin >> t;  // 测试用例数量
        while (t--) {
            cin >> n;
            // 读取当前书籍序列
            for (int i = 0; i < n; i++) cin >> a[i];
            
            // 迭代加深搜索
            int depth = 0;  // 当前深度限制
            // 深度0~4搜索,找到最小转置次数
            while (depth < 5 && !dfs(0, depth)) depth++;
            
            // 输出结果
            depth >= 5 ? cout << "5 or more" << endl : cout << depth << endl;
        }
        return 0;
    }
    
    • 1

    信息

    ID
    2461
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    4
    已通过
    1
    上传者