编辑书稿——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;
}