180. 排书

给定 n 本书,编号为 1∼n。

在初始状态下,书是任意排列的。

在每一次操作中,可以抽取其中连续的一段,再把这段插入到其他某个位置。

我们的目标状态是把书按照 1∼n 的顺序依次排列。

求最少需要多少次操作。

输入格式

第一行包含整数 T,表示共有 T 组测试数据。

每组数据包含两行,第一行为整数 n,表示书的数量。

第二行为 n 个整数,表示 1∼n 的一种任意排列。

同行数之间用空格隔开。

输出格式

每组数据输出一个最少操作次数。

如果最少操作次数大于或等于 5 次,则输出 5 or more

每个结果占一行。

数据范围

1≤n≤15

输入样例:
3
6
1 3 4 6 2 5
5
5 4 3 2 1
10
6 8 5 3 4 7 2 9 1 10
输出样例:
2
3
5 or more
代码:
/*
IDA*这个算法的思想其实比A*要好理解很多
简单来说就是我们开一个估计函数这个估计函数的返回值一定比真实值要小于等于这样我们就可以利用这个进行最优化减枝
如果估计函数都无法完成那么真实值一定也无法完成可以直接剪掉
这题由于步数很小答案可能会很大所以我们再用一下之前讲的迭代加深的思想来优化一下搜索
这题我们搜索可以去枚举我们选择的长度以及我们放的位置去搜索
这题我们其实可以不用往前放因为我们往前放会对应前面的一种选法完全没有必要
这题由于每次我们最多改变3个位置的关系所以假设我们有k个错误前后关系那么(k+2)/3就是我们最多要操作的次数
可以看到估计函数的影子了所以我们这题再用IDA*来优化
我们可以用这个(k+2)/3来当成估计函数
*/
#include <bits/stdc++.h>
using namespace std;

const int N = 15;

int n;
int q[N];
int w[5][N];

int f() // 估价函数 每次移动最多改三个点的后继
{
    int cnt = 0;
    for (int i = 1; i < n; i++)
    {
        if (q[i] != q[i - 1] + 1)
            cnt++;
    }
    return (cnt + 2) / 3; // 除以三上取整
}

bool dfs(int u, int depth)
{
    if (f() == 0) // 不需要改 则成功
        return true;

    if (u + f() > depth) // 估价函数剪枝
        return false;

    for (int len = 1; len <= n - 1; len++)
    {
        for (int i = 0; i + len - 1 < n; i++)
        {
            int j = i + len - 1;
            for (int k = j + 1; k < n; k++)
            {
                memcpy(w[u], q, sizeof(q));
                int x, y;
                for (x = j + 1, y = i; x <= k; x++, y++)
                    q[y] = w[u][x];
                for (x = i; x <= j; x++, y++)
                    q[y] = w[u][x];
                if (dfs(u + 1, depth))
                    return true;
                memcpy(q, w[u], sizeof q);
            }
        }
    }
    return false;
}

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        cin >> n;
        for (int i = 0; i < n; i++)
            cin >> q[i];

        int depth = 0;
        while (depth < 5 && !dfs(0, depth)) //0变到1,表示操作一次,变到2,表示操作两次,依次类推。
            depth++;
        if (depth >= 5)
            puts("5 or more");
        else
            cout << depth << endl;
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

追寻远方的人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值