题目大意:有N(1 ≤ N ≤ 15)本书,每本与每本的高度都不一样。现在可以按照以下的办法整理书:抽出一摞书,再保持原来的顺序插进一个位置。这样的话我们称之为“一次操作”。现在你需要求出至少需要经过几次操作才能让书变成按高度升序的状态。如果需要5次或者多于5次,只需要输出“5 or more”。
解法:IDA*是一种特殊的dfs,每次dfs都设置一个阀值,一旦解的深度超出阀值就回溯。只不过IDA*加上了对当前状态的估计——一旦已经走过的步数加上估计步数超出阀值,就进行回溯。若在此阀值下无解,则迭代增加阀值,只有找到的解深度是最小的。当然,“估价函数”与实际深度越接近越好,而且应该小于等于实际深度。
这个题的估价函数很经典——因为一次操作最多改变3个值的后继值,而最终状态要求每个位置的后继值都比自己大1,因此估价函数为当前状态下每个值的后继值错误的个数除以3。
import java.util.Scanner;
public class Main{
int h() {
int res = 0;
for(int i=1;i<n;i++)
if(arr[i]+1!=arr[i+1]) res++;
if(arr[n]!=n) res++;
return (res+2)/3;
}
long hash(){
long res=0;
return res;
}
int lim, n;
int arr[]=new int[20];
boolean flag;
int dfs(int d) {
int h = h();
if (h == 0){
flag=true;
return d;
}
if (h + d > lim)
return h + d;
int temp[]=new int[n+1];
for(int i=1;i<=n;i++)
temp[i]=arr[i];
int res = 1 << 28;
for (int i = 1;i<n;i++)
for (int j= i;j<n;j++)
for (int k = j + 1; k <= n; k++)
{
int cnt=i;
for(int p=j+1;p<=k;p++)
arr[cnt++]=temp[p];
for(int p=i;p<=j;p++)
arr[cnt++]=temp[p];
int tp=dfs(d+1);
if(flag)
return tp;
else
res=Math.min(tp, res);
cnt=i;
for(int p=i;p<=j;p++)
arr[cnt++]=temp[p];
for(int p=j+1;p<=k;p++)
arr[cnt++]=temp[p];
}
return res;
}
void astar(){
lim=h();
flag=false;
while(lim<=4&&!flag)
lim=dfs(0);
if(flag)
System.out.println(lim);
else
System.out.println("5 or more");
}
Scanner scan=new Scanner(System.in);
void run(){
int cas=scan.nextInt();
while(cas-->0){
n=scan.nextInt();
for(int i=1;i<=n;i++)
arr[i]=scan.nextInt();
astar();
}
}
public static void main(String[] args) {
new Main().run();
}
}