1. 程式人生 > 其它 >[一本通1684]翻轉序列 題解

[一本通1684]翻轉序列 題解

給定一個$1\sim n$的排列$x$,每次你可以將$x_1\sim x_i$翻轉。你需要求出將序列變為升序的最小操作次數。有多組資料。.......本題從求最小步數就可以知道,需要使用IDA。而我們最終是要求一個翻轉成遞增序列。

題目描述

給定一個\(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;
    }
}