模拟赛 序列

题目大意

给定一个 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 n25

对于测试点 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 (12i21) 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 
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;
}

你可能感兴趣的:(题解,深度优先,c++,题解,迭代加深)