[一本通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;
}
}