对于一个小于16的n,我们首先肯定能够想到搜索,关键是搜什么,怎么搜,用什么方法搜?
我们首先面临的问题是铁盘大小,对于这个问题直接离散化,使铁盘成为一个1~n的全排列
第二个问题,直接dfs会活生生的爆掉,bfs会让你处于MLE无法自拔,我们于是用我们的优化技巧迭代加深。确定一个搜索深度,用\Theta(1)Θ(1)空间复杂度去求得答案
然后就会T爆
考虑剪枝。我们通读题面,发现我们一次操作翻转了1~x的铁盘,我们发现,啊,我们最多改变两个铁盘之差,按我们的最优方法,如果现在还有y个相邻铁盘差值不为1,我们至少执行y次操作
但是注意,我们翻转1~n也相当于改变了差值,所以说我们需要将moved_{n+1}moved
n+1
设成n+1n+1
所以说我们在一开始的时候预估步数,然后搜索就完了
其实这就是IDA*IDA∗,预估步数就是预估函数,在这里能起到剪枝的作用
#include<cstdio>
#include<algorithm>
#include<queue>
#include<bitset>
using namespace std;
int n,iron[55],moved[55],depth=0;
int Abs(int x){return x<0?-x:x;}
void init(){for(int i=1;i<=n;++i) moved[i]=iron[i];}
bool iddfs(int now)
{
int error=0;
for(int i=1;i<=n;++i) if(Abs(moved[i]-moved[i+1])>1) ++error;
if(now+error>depth) return false;//预估步数,加上已有步数进行计算
if(now==depth)
{
for(int i=1;i<=n;++i) if(moved[i]!=i) return false;
return true;
}//判断是否合法
bool flag=false;
for(int i=2;i<=n;++i)
{
for(int j=1;j<=(i>>1);++j) swap(moved[i-j+1],moved[j]);
flag|=iddfs(now+1);
for(int j=1;j<=(i>>1);++j) swap(moved[i-j+1],moved[j]);//进行搜索
if(flag) return true;
}
return false;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&iron[i]),moved[i]=iron[i];
sort(moved+1,moved+1+n);
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(iron[i]==moved[j])
{
iron[i]=j;
break;
}//离散化
moved[n+1]=n+1;
do init(),++depth; while(!iddfs(0));//id
printf("%d",depth);
return 0;
}