Loading

编辑书稿——UVa 11212 IDA星

题目描述

你有一篇由n(2≤n≤9)个自然段组成的文章,希望将它们排列成1, 2,…, n。可以用Ctrl+X(剪切)和Ctrl+V(粘贴)快捷键来完成任务。每次可以剪切一段连续的自然段,粘贴时按照顺序粘贴。注意,剪贴板只有一个,所以不能连续剪切两次,只能剪切和粘贴交替。

例如,为了将{2,4,1,5,3,6}变为升序,可以剪切1将其放到2前,然后剪切3将其放到4前。再如,对于排列{3,4,5,1,2},只需一次剪切和一次粘贴即可——将{3,4,5}放在{1,2}后,或者将{1,2}放在{3,4,5}前。

输入输出

包含多组输入,每组第一行是n,第二行是1~n的一个排列,当n为0时输入结束

输出每组输入对应的最少剪切数。

Sample Input
6
2 4 1 5 3 6
5
3 4 5 1 2
0

Sample Output
Case 1: 2
Case 2: 1

思路

9个数的排列有\(9!=362880\)个,对于一个排列,我们需要遍历所有的剪切粘贴可能,你可以剪切任意长度的序列到任意位置,这个数字再乘上排列数就很大了。

使用IDA*来做这道题目,深度最大为8,最多也就8次剪贴。每次把所有本次可能的剪贴情况遍历,如果没有有序的就把遍历深度加深,也就是在一次剪贴的基础下再加一次,最后肯定只需要看便利了多深就是答案了。

假设\(h\)时当前后置字符不正确的字符个数,\(d\)为当前深度,\(maxd\)为最大允许的迭代深度,那么\((maxd-d)*3<h\)的话就可以剪枝了,这是这道题的启发函数。因为每次剪贴最多改变三个字符的后续字符,所以最多能让3个字符的后置字符从不正确变为正确。

感觉困难的地方就是递归遍历所有的状态。

代码

#include "iostream"
#include "cstdio"
#include "cstring"
#define MAXN 10

using namespace std;
int a[MAXN];
int n;

int h() {
    int cnt = 0;
    for (int i = 0; i < n - 1; i++)
        if (a[i + 1] != a[i] + 1)cnt++;
    if (a[n - 1] != n)cnt++;
    return cnt;
}

bool is_ordered() {
    for (int i = 0; i < n; i++)if (a[i] != i + 1)return false;
    return true;
}

bool dfs(int d, int maxd) {
    if (3 * d + h() > 3 * maxd)return false;
    if (is_ordered())
        return true;
    /*
    original是原排列的备份,side是未选中的部分,每次选中一部分进行剪切会把原来的文字分成三部分
    [before] [selected] [after]
    side记录的是before和after中的文字
    */
    int original[MAXN],side[MAXN];
    memcpy(original, a, sizeof(a));

    for (int s = 0; s < n; s++) {//枚举起始点
        for (int e = s; e < n; e++) {//枚举终止点
            int ith=1;
            for (int k = 0; k < s; k++)side[ith++] = original[k];
            for (int k = e + 1; k < n; k++)side[ith++] = original[k];
            for (int k = 0; k < ith; k++) {// 枚举出所有组合 k意思为插到第k个位置
                int ith2 = 0; int i;
                for (i = 1; i <= k; i++)a[ith2++] = side[i];
                for (i = s; i <= e; i++)a[ith2++] = original[i];
                for (i = k+1; i < ith ;i++)a[ith2++] = side[i];
                if (dfs(d + 1, maxd))return true;
            }
        }
    }
    return false;
}
int main() {
   int kcase = 1;
    while (scanf("%d", &n) != EOF && n != 0) {
        for (int i = 0; i < n; i++)scanf("%d", &a[i]);
        for (int maxd = 0; maxd < n; maxd++) {
            if (dfs(0, maxd)) {
                printf("Case %d: %d\n", kcase++,maxd);
                break;
            }
        }
    }
    return 0;
}

posted @ 2020-12-06 12:35  yudoge  阅读(119)  评论(0编辑  收藏  举报