P3750奶牛拍照
问题描述
约翰的n(1<=n<=15)头奶牛正开心地站成一排,等待约翰拍集体照。
在大家都长时间保持微笑,等待约翰按下快门的时候,约翰突然停了下来,因为他发现奶牛们并没有按照身高由低到高的顺序排列(每头奶牛的高度都不相同),这样的话就拍不出满意的照片,于是约翰决定调整一下奶牛们的位置。
这下可扫了奶牛们的兴,大家决定为难一下约翰。奶牛们告诉约翰,要调整位置可以,每次只能选择连续一段奶牛出来,然后将这一段整体插入到剩下队伍的任意位置。而且要求约翰操作的次数尽可能少。
你能否帮助约翰算算,最少几次操作就能将奶牛们调整成由矮到高的排列顺序。
输入格式
第一行,一个整数n,表示奶牛的数量
第二行,n个空格间隔的整数,描述了目前奶牛排列的情况,每个整数表示对应奶牛的身高排名。
输出格式
一行,一个整数,表示最少需要的调整次数。
如果次数大于4,输出"-1"
样例输入
6
1 3 4 6 2 5
样例输出
2
提示
样例说明:
1 3 4 6 2 5
第一步,把[2 5]这一段选出,插入到4的后面,得到1 3 4 [2 5] 6
第二步,把[2]这一段选出,插入到1的后面,得到1 [2] 3 4 5 6
这题的主要思路就是枚举要移动的区间以及目标位置,但是状态数量实在太多,朴素dfs难以承受,因此考虑剪枝
估价函数h()表示将当前序列变为单调递增的最少移动次数,只要这个最少移动次数加上当前移动次数大于了规定的深度就停止搜索
难点就在于估价函数上,h()并不单纯是要移动的元素的个数,因为移动了一段元素可能会影响其他元素,而最少会影响3个元素,因此最终的个数应该除以三并且向上取整,这样就是一个完整的IDA*算法了
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
int n,s[20],depth,a[20],b[20];
int geth(){
int temp=0,i;
if(s[1]!=1)temp++;
for(i=2;i<=n;i++)
if(s[i]!=s[i-1]+1)temp++;
return ceil((double)temp/3.0);
}
void move_r(int x,int y,int t){
int i;
for(i=x;i<=y;i++)a[i]=s[i];
for(i=y+1;i<=t;i++)s[i-y+x-1]=s[i];
for(i=x;i<=y;i++)s[t+i-y]=a[i];
}
void move_l(int x,int y,int t){
int i;
for(i=x;i<=y;i++)a[i]=s[i];
for(i=x-1;i>=t+1;i--)s[i+y-x+1]=s[i];
for(i=x;i<=y;i++)s[t+i-x+1]=a[i];
}
bool dfs(int k){
int h=geth();
if(k+h>depth)return 0;
if(k==depth&&h==0)return 1;
int i,j,q;
for(i=1;i<=n;i++)
for(j=i;j<=n;j++)
for(q=j+1;q<=n;q++){
move_r(i,j,q);
if(dfs(k+1))return 1;
move_l(i+q-j,q,i-1);
}
return 0;
}
int main(){
cin>>n;
int i;
for(i=1;i<=n;i++)cin>>s[i];
for(depth=0;depth<=4;depth++)
if(dfs(0)){
printf("%d",depth);
return 0;
}
puts("-1");
}