https://www.luogu.org/problem/show?pid=T2485
题目背景
汉诺塔升级了
题目描述
现在我们有N个圆盘和N个柱子,每个圆盘大小都不一样,大的圆盘不能放在小的圆盘上面,N个柱子从左向右排成一排。每次你可以将一个柱子的最上面的圆盘移动到右边或者左边的柱子上(如果移动是合法的话)。现在告诉你初始时的状态,你希望用最少的步数将第i大的盘子移动到第i根柱子上,问最小步数。
输入输出格式
输入格式:
第一行一个整数T,代表询问的组数。
接下来T组数据,每组数据第一行一个整数N。
接下来一行N个正整数,代表每个柱子上圆盘的大小。
输出格式:
输出共T行,代表每次的答案。如果方案不存在,输出-1;
输入输出样例
输入样例#1:
4
3
2 1 3
2
7 8
2
10000 1000
3
97 96 95
输出样例#1:
4
0
-1
20
说明
对于70%的数据,N的值都是相等的。
对于100%的数据,1≤T≤6 x 10^3 ; 1≤N≤7。
其实这个题其实是打表啊…..用BFS打表,然后对应询问直接输出,时空复杂度大丈夫。
对于读入进来的数据,离散化一下,用z数组记录这个盘子是第几大。
用bit数组记录位,这个是当你移动盘子的时候有用的….(具体我也说不太清楚,看代码理解理解吧。)
我们开一个top和place数组,分别记录第i个柱子的最上面的盘子,和当前盘子在哪根柱子上,
预处da理biao的时候,首先从初始状态开始搜(1,12,123,1234……1234567)
每搜到一个新的可能状态,就把这个状态记录下来。(试想一下,从某个给定状态到初始状态的步数==从初始状态的步数到给定状态的步数)
细节处理的话,直接看代码吧。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=;
int n,top[],place[],bit[];//最上面|每个盘子位置 |进制保存
int res[maxn];//答案
int q[maxn];//队列 int front,tail;//队列头尾指针
int w[],z[];//z为离散化后的大小
bool use[maxn];
bool cmp(int a,int b)
{
return w[a]<w[b];
}//按大小排序
void analyze(int s)
{
int x=;
int ss=s;
for (int a=;a<=;a++)
top[a]=;//最上面的
while (ss)
{
x++;
place[x]=ss%;//初始盘子位置(处理是反着的)
ss/=;
}
reverse(place+,place+x+);//逆转 (一个奇怪的函数) for (int a=x;a>=;a--)
top[place[a]]=a;
//初始最上面位置 即1 2 3 4 5 6 7 for (int a=;a<=x;a++)
{
if (a==top[place[a]])//a在某个柱子的最上面//移动的时候,是直接往左移动一个柱子或直接往右移一个柱子
{
int p=place[a];//那个柱
if (p!= && (a<top[p-] || !top[p-]))//小或者空
{
int news=s-bit[x-a];//新状态
if (!use[news])
{
q[++tail]=news;
use[news]=true;
res[news]=res[s]+;//因为是宽搜,第一次搜到的就是最小的。
}
}//左移
if (p!=x && (a<top[p+] || !top[p+]))
{
int news=s+bit[x-a];
if (!use[news])
{
q[++tail]=news;//orz 钟小鸟
use[news]=true;
res[news]=res[s]+;
}
}//右移
}
} } int main()
{
// freopen("huakai.in","r",stdin);
// freopen("huakai.out","w",stdout);
front=,tail=;
int status=;
bit[]=;
for(int a=;a<=;a++)
{
bit[a]=bit[a-]*;
status=status*+a;//1 12 123 1234
q[++tail]=status;//1 12 134 1234
use[status]=true;//1 12 123 1234
}//bit 10 100 1000 10000...
for(;front<=tail;)
{
int s=q[front++];//
analyze(s);//
}
printf("%d",tail);
int t;
scanf("%d",&t);
for(;t--;)
{
scanf("%d",&n); for (int a=;a<=n;a++)
scanf("%d",&w[a]),z[a]=a;//按编号sort
sort(z+,z+n+,cmp); int s=;
for (int a=;a<=n;a++)
s=s*+z[a];//盘子大小序列 if (!use[s]) printf("-1\n");//移动不到
else printf("%d\n",res[s]);
}
return ;
}