1. 程式人生 > 實用技巧 >編輯書稿——UVa 11212 IDA星

編輯書稿——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;
}