题意:给n个数字,最少需要几次排成递增序列,可片段的插入。
思路:
(1)枚举:因为不确定最少几次排好,所以枚举步数:最多需8步,n<=9 .
(2)搜索:每次枚举当前要达到的深度(步数),然后进行dfs搜索
dfs:用俩层循环枚举剪切片段的起始点和终止点,然后在剪切剩下的片段中将剪切片段依次在每一个位置的前面插入组成一个新序列后进行判断。
(3)判断: 每次组成一个新序列后进行判断是否为递增,是直接返回true。结束枚举。
(4)剪枝:求出每次的新序列的后继不正确的数字个数h ,可以证明每次剪切时h最多减少3, 例如:序列 9 8 7 6 5 4 3 2 1 剪切部分:765 将 765 插入到 3与2之间,则 8 连接的是4 ,3连接的是7 ,2连接的是5 ,出现3处。
所以:当3d+h>3maxd时可以剪枝,其中d为当前深度,maxd为深度限制 。
IDA* : 迭代深度搜索 + 超出范围的剪枝
参考:某大神链接
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 10;
int n,a[maxn];
bool is_sort(){//判断是否是递增序列
for(int i=0;i<n;i++) if(a[i] != i+1) return false;
return true;
}
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 dfs(int d,int maxd){
if(3*d + h() > 3*maxd) return false;//剪枝
if(is_sort()) return true;
int original[maxn],part[maxn];
memcpy(original,a,sizeof(a));//保留原序列
for(int i=0;i<n;i++){//枚举起始点
for(int j=i;j<n;j++){//枚举终止点
int cnt_part = 1;//这里必须从1开始记录,因为下面插入的时候是从k前插入
for(int k=0;k<n;k++) if(k < i || k > j) part[cnt_part++] = original[k];//将剪切判断除外的数字保留
//将剪切部分依次插入原序列的每一个位置上,放入a数组中!,在k前插入!
for(int k=0;k<cnt_part;k++){
int cnt_whole = 0;
for(int p=1;p<=k;p++) a[cnt_whole++] = part[p];//起始点的部分
for(int p=i;p<=j;p++) a[cnt_whole++] = original[p];//将剪切部分插入
for(int p=k+1;p<cnt_part;p++) a[cnt_whole++] = part[p];//终止点的部分
if(dfs(d+1,maxd)) return true;
}
}
}
return false;
}
int solve(){
if(is_sort()) return 0;
for(int maxd=1;maxd<9;maxd++){//枚举深度
if(dfs(0,maxd)) return maxd;
}
return 5;
}
int main()
{
int kcase = 0;
while(scanf("%d",&n)!=EOF && n){
for(int i=0;i<n;i++) scanf("%d",&a[i]);
printf("Case %d: %d\n",++kcase,solve());
}
return 0;
}