题目链接:
题意:
有一个类似汉诺塔的游戏,共有n个柱子和n个大小不同的盘子
每个盘子可以移动到相邻的柱子上去,同时盘子可以叠加,但要求小的盘子放在大的盘子上面
问从当前状态到目标状态(n个盘子从小到大依次排列在n个柱子上)所需要移动的最小次数
解题思路:
首先要离散化(1~n)
最小步数------bfs
多个起点,固定终点---------------预处理,反向bfs,同时进行记忆化搜索
然后就是最关键的问题:如何在搜索中记录每一个状态(hash)
既然一根柱子可以放多个盘子,那就不能按每个位置的盘子编号叠加来hash
我们可以通过每个盘子所在的位置叠加hash,最多7个盘子即8^7可保存所有状态
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define maxn 10005000
using namespace std;
int dp[maxn];
int pos[10],dig[10];
void bfs(int x)
{
queue<int>q;
int head,temp,t,state=0;
dig[x]=1;
for(int i=x-1;i>=1;i--)
dig[i]=dig[i+1]*8;
for(int i=1;i<=x;i++)
state=state*8+i;
q.push(state);
dp[state]=0;
while(!q.empty())
{
t=head=q.front();
q.pop();
memset(pos,0,sizeof(pos));
for(int i=x;i>=1;i--,t/=8)
pos[t%8]=i;
for(int i=1;i<=x;i++)
{
if(pos[i]==0)
continue;
if(i>1&&(pos[i]<pos[i-1]||!pos[i-1])){
temp=head-dig[pos[i]];
if(dp[temp]==-1)
{
dp[temp]=dp[head]+1;
q.push(temp);
}
}
if(i<x&&(pos[i]<pos[i+1]||!pos[i+1])){
temp=head+dig[pos[i]];
if(dp[temp]==-1)
{
dp[temp]=dp[head]+1;
q.push(temp);
}
}
}
}
}
void init()
{
memset(dp,-1,sizeof(dp));
for(int i=1;i<=7;i++)
bfs(i);
}
int main()
{
init();
int T,n,st;
int v[10],loc[10500];
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&v[i]);
loc[v[i]]=i;
}
st=0;
sort(v+1,v+1+n);
for(int i=1;i<=n;i++)
st=st*8+loc[v[i]];
printf("%d\n",dp[st]);
}
return 0;
}