题目链接:
题目大意:
你有一篇由n(2<=n<=9) 个自然段组成的文章,希望将他们排列成1, 2, 3, …,n.可以用剪切和粘贴快捷键来完成任务.每次可以剪切一段连续的自然段,粘贴时按照顺序粘贴,注意,剪切板只有一个,所以不能连续剪切两次,只能剪切和粘贴交替.
例如,为了将{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}前.求最少操作次数
思路:
-----------------摘自算法竞赛,入门经典
策略一:
每次只剪切一段连续的数字.例如,不要剪切24这样的数字不连续的片段.
策略二:
假设剪切片段的第一个数字为a,最后一个数字为b,要么把这个片段粘贴倒a-1 的下一个位置,要么粘贴倒b+1的前一个位置.
策略三:
永远不要"破坏"一个已经连续的排列的数字片段.例如不要把 1234 中的2, 3剪切出来,三种策略都能缩小状态空间,但是他们都不是正确的,很对程序都无法得到"54321"这的正确结果3(54321->32541->34125->12345)
本题可以用idea*算法求解,不难发现n<=9时最多只需8步,因此深度上限8.
IDEA*的关键在于启发函数,考虑后继不正确的数字个数h,可以证明每次剪切是H最多减少3,因此当3d+h>3maxd时可以剪枝,其中d为当前深度,maxd为的深度限制.
/*************************************************************************
> File Name: uva_11212.cpp
> Author: dulun
> Mail: dulun@xiyoulinux.org
> Created Time: 2016年04月15日 星期五 18时01分35秒
************************************************************************/
//Rey
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn = 9;
int n,a[maxn];
inline bool check()
{
for(int i = 1; i < n; i++){
if(a[i] <= a[i-1]) return false;
}
return true;
}
inline int h()
{
int cnt = 0;
for(int i = 1; i < n; i++)
if(a[i] != a[i-1]+1) cnt++;
return cnt;
}
int maxd;
const int intsz = sizeof(int);
const int asz = sizeof(a);
bool dfs(int d)
{
if(3*d + h() > 3*maxd) return false;
if(check()) return true;
int old[maxn];//保存a
memcpy(old,a,asz);
int b[maxn];//剪切板
for(int i = 0; i < n; i++) if( i == 0 || old[i] != old[i-1] + 1) //策略3 选择尽量长的连续片段 剪切的起点
for(int j = i; j < n; j++) { //终点 和 策略2不同选取片段可以不连续
while(j+1 < n && old[j+1] == old[j] + 1)j++;//策略3
memcpy(b,old+i,intsz*(j-i+1));
//剪切移动片段
for(int k = j+1;k < n;k++){//由于对称性,只要往后贴就行了
while(k+1 < n && old[k+1] == old[k] + 1)k++;//策略3 不破坏
memcpy(a+i,old+j+1,intsz*(k-j));
memcpy(a+i+k-j,b,intsz*(j-i+1));
if(dfs(d+1))return true;
//恢复
memcpy(a,old,asz);
}
}
return false;
}
inline int solve()
{
if(check())return 0;
for(maxd = 1; maxd < 5 ;maxd++)
if(dfs(0)) return maxd;
return 5;
}
int main()
{
int Cas = 0;
while(~scanf("%d",&n)&&n) {
for(int i = 0; i < n; i++)
scanf("%d",a+i);
int ans = solve();
printf("Case %d: %d\n",++Cas,ans);
}
return 0;
}