【题目链接】
UVa 11212 Editing a book
【题目描述】
你有一篇由$n$个自然段组成的文章,希望将他们排列成1,2,3 ... n。可以用剪切和粘贴来完成任务,每次可以剪切一个或多个连续的自然段,粘贴时按照顺序粘贴。注意,剪贴板只有一个,所以能连续剪切两次,只能剪切和粘贴交替,求最少操作次数。 (1 < n < 10)
【解题思路】
IDA*的水题啦。 剪枝的话,设h为后继不正确的数字个数,每次剪切粘贴后h最多减少3,这是因为每次操作后至多有三个数的后继发生了变化,所以当$3 * d + h > limit * 3$可以剪枝。
【AC代码】
没把move的函数挑出来写,就这样吧
#include <cstdio>
#include <algorithm>
#include <cstring>
#define MAXN 10
int n;
int a[MAXN];
int limit;
inline bool is_sorted(){
for(int i = 0; i < n - 1; i++)
if(a[i] >= a[i + 1]) return false;
return true;
}
inline int wrongSuccNum(){
int count = 0;
for(int i = 0; i < n - 1; i++)
if(a[i] + 1 != a[i + 1]) count++;
if(a[n - 1] != n) count++;
return count;
}
bool dfs(int d){
if((limit - d) * 3 < wrongSuccNum()) return false;
if(is_sorted()) return true;
int oldA[MAXN];
int b[MAXN];
memcpy(oldA, a, sizeof a);
for(int l = 0; l < n; l++){
for(int r = l; r < n; r++){
// [l, r]
int count = 0;
for(int k = 0; k < n; k++) if(k < l || k > r) b[count++] = a[k];
for(int k = 0; k <= count; k++){
int pos = 0;
for(int i = 0; i < k; i++) a[pos++] = b[i];
for(int i = l; i <= r; i++) a[pos++] = oldA[i];
for(int i = k; i < count; i++) a[pos++] = b[i];
if(dfs(d + 1)) return true;
memcpy(a, oldA, sizeof a); // 记得一定要恢复现场
}
}
}
return false;
}
inline int solve(){
if(is_sorted()) return 0; // 这里注意特判
else for(limit = 1; ; limit++){
if(dfs(0)) return limit;
}
}
int main(){
for(int kase = 1; scanf("%d", &n) == 1 && n; kase++){
for(int i = 0; i < n; i++) scanf("%d", a + i);
printf("Case %d: %d\n", kase, solve());
}
}
就这样啦