题目大意
给定一个 1 1 1 到 n n n 的排列 x x x,每次你可以将 x 1 x_1 x1 到 x n x_n xn 翻转,你需要求出将原序列变为升序的最小操作次数。
题目有多组数据。
数据范围
对于 100 / 100/% 100/ 的测试数据, t = 5 t=5 t=5, n ≤ 25 n\leq25 n≤25。
对于测试点 1 1 1, 2 2 2, n = 5 n=5 n=5。
对于测试点 3 3 3, 4 4 4, n = 6 n=6 n=6。
对于测试点 5 5 5, 6 6 6, n = 7 n=7 n=7。
对于测试点 7 7 7, 8 8 8, 9 9 9, n = 8 n=8 n=8。
对于测试点 10 10 10, n = 9 n=9 n=9。
对于测试点 11 11 11, n = 10 n=10 n=10。
对于测试点 i ( 12 ≤ i ≤ 21 ) i\ (12\leq i\leq 21) i (12≤i≤21), n = i n=i n=i。
对于测试点 22 22 22, 23 23 23, n = 22 n=22 n=22。
对于测试点 24 24 24, 25 25 25, n = 23 n=23 n=23。
思路
首先看到这道题,并没有什么很好的思路。
看到数据范围,比较小,考虑暴力。
发现光搜索是过不了的,宽搜会 MLE,深搜会 TLE。
于是考虑迭代加深搜索。
枚举操作的上限,然后深搜。
有一个剪枝,计算出相邻两个元素差大于一的个数 k k k,则想要变为升序则至少需要 k k k 次操作,于是可以再搜索中计算当前还有几个不相邻,如果当前操作的次数加上当前不相邻的个数大于枚举的答案,则直接返回即可。
实现参考下面代码。
代码
#include <bits/stdc++.h>
using namespace std;
int T, n, a[30], b[30], ans, sum, k;
void flip(int n) {
for (int i = 1; i <= n / 2; i++)
swap(a[i], a[n - i + 1]);
}
bool dfs(int x, int num) {//num是当前有多少个不相邻的,x是当前操作数
if (x + num > k)//剪枝优化
return false;
bool flag = true;
for (int i = 1; i <= n; i++)
if (a[i] != i) {
flag = false;
break;
}
if (flag)
return flag;
for (int i = n; i >= 2; i--) {
int l = (i < n & abs(a[i] - a[i + 1]) == 1) - (i < n & abs(a[1] - a[i + 1]) == 1);
//计算当前翻转后有不相邻的个数的变化值
flip(i);
flag = dfs(x + 1, num + l);
flip(i);
if (flag)
return flag;
}
return false;
}
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
int num = 0;
for (int i = 1; i < n; i++)
if (abs(a[i] - a[i + 1]) > 1)
num++;
dfs(1, 0);
for (k = 0; ~k; k++)
if (dfs(0, num)) {
printf("%d\n", k);
//因为k从小到大枚举,所以当前枚举到的第一个满足的k即为答案
break;
}
}
return 0;
}