[一本通1684]翻转序列 题解

题目描述

给定一个\(1\sim n\)的排列\(x\),每次你可以将\(x_1\sim x_i\)翻转。你需要求出将序列变为升序的最小操作次数。有多组数据。

输入

第一行一个整数\(t\)表示数据组数。
每组数据第一行一个整数\(n\),第二行\(n\)个整数\(x_1\sim x_n\)

输出

每组数据输出一行一个整数表示答案。

输入样例

1
8
8 6 1 3 2 4 5 7

输出样例

7

提示

数据规模与约定

对于100%的测试数据,\(t=5,n≤23\)
对于测试点\(1,2,n=5\)
对于测试点\(3,4,n=6\)
对于测试点\(5,6,n=7\)
对于测试点\(7,8,9,n=8\)
对于测试点\(10,n=9\)
对于测试点\(11,n=10\)
对于测试点\(i (12≤i≤21),n=i\)
对于测试点\(22,23,n=22\)
对于测试点\(24,25,n=23\)

思路

本题从求最小步数就可以知道,需要使用IDA。而我们最终是要求一个翻转成递增序列。
先从最简单粗暴的方法想起。
如果第1大的数大在第一位,只需要翻转一次就可以归位。如果不在第一位,先翻转到第一位,两次翻转也能归位。这样我们就可以推出IDA函数

代码

#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;

const int N = 24;

int num[N];

int n;

inline int IDA()
{
    int cnt = 0;
    for (int i = 1; i < n; i++)
        if (abs(num[i] - num[i + 1]) > 1)
            cnt++;
    return cnt;
}

bool ac = false;

int ans = 1e9;

void dfs(int dep, int last)
{
    if (ac)
        return;
    if (dep + IDA() > ans)
        return;

    bool tag = false; //完成标签
    for (int i = 1; i < n; i++)
        if (num[i] != num[i + 1] - 1)
        {
            tag = true;
            break;
        }
    if (!tag) //完成
    {
        ac = true;
        return;
    }
    for (int i = 2; i <= n; i++)
    {
        if (i != last)
        {
            reverse(num + 1, num + 1 + i);
            dfs(dep + 1, i);
            reverse(num + 1, num + 1 + i);
        }
    }
}

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        cin >> n;
        for (int i = 1; i <= n; i++)
        {
            cin >> num[i];
        }
        for (ans = 0;; ans++)
        {
            ac = false;
            dfs(0, 0);
            if (ac)
                break;
        }
        cout << ans << endl;
    }
}
posted @ 2021-08-12 20:57  Icys  阅读(256)  评论(0编辑  收藏  举报