如果一个 3 × 3 的矩阵中,整数 1-9 中的每个都恰好出现一次,我们称这个矩阵为一个幻 方。
我们可以对一个幻方进行一些操作。具体来说,我们可以
• 选择幻方的一行,整体向右移动一格,并将最右侧的数字移到最左边;或者
• 选择幻方的一列,整体向下移动一格,并将最下侧的数字移到最上面。
例如,下面两个操作分别是一种合法的行操作和列操作:
显然,一个合法的幻方经过一次操作后一定还是合法的幻方。
给定幻方的初始状态,请问,最少要经过多少次变换,才能变成最终状态?
输入描述:
第一行一个整数 T (1 ≤ T ≤ 200000),表示测试用例的数量。
接下来有 T 组测试用例,每组测试用例前有一个空行。每组样例的前 3 行为幻方的初始状态,后 3 行为幻方的最终状态。每行的数字之间没有空格。
保证初始状态和最终状态都是合法的幻方。
输出描述:
对于每组测试用例在一行内输出一个整数,表示答案。如果不可能从起始状态转变为最终状态,输出 impossible。
样例输入:
4
123
456
789
231
456
789
457
213
689
257
361
489
927
641
358
297
651
384
123
456
789
123
456
789
样例输出:
2
3
impossible
0
分析:
$广搜即可。不过因为是t组输入,即每一次广搜的最坏次数为9!,故每一次都广搜的话会超时 $
我们可以预处理出123 456 789 到所有状态的的最小次数,然后对于题目要求输入的初始状态和最终状态,只需把初始状态对应位置的数字都对应为123 456 789,并且把最终状态的按照这种规则转化一下。那么只需求123 456 789这种状态到转换后的最终状态的最短距离即可。因为预处理了,可以O(1)时间内求出来。
故可分为下列步骤
-
预处理123 456 789到其他所有状态的最少操作次数(广搜即可)
需要注意以下问题
- 对于幻方的状态可以用每一行拼接后的字符串表示
- 定义幻方旋转第行和旋转第列的操作
-
将输入的最初状态转换为123 456 789转换并记录下规则
-
将输入的最终状态按照 第二步 的规则转化为可以求的最终状态
-
O(1)出答案即可
代码:
#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
struct HuanF{
string s;
int step;
HuanF Row_right(int k)//第k行向右旋转
{
HuanF ans;
ans.s=s;
char c=ans.s[3*k+2];
ans.s[3*k+2]=ans.s[3*k+1];
ans.s[3*k+1]=ans.s[3*k];
ans.s[3*k]=c;
return ans;
}
HuanF Colu_down(int k)
{
HuanF ans;
ans.s=s;
char c=ans.s[k+6];
ans.s[k+6]=ans.s[k+3];
ans.s[k+3]=ans.s[k];
ans.s[k]=c;
return ans;
}
void Print()
{
for(int i=0;i<s.length();++i)
{
printf("%c%c",s[i],i%3==2?'
':' ');
}
printf("step=%d
",step);
}
};
bool operator <(const HuanF &a ,const HuanF& b)
{
return a.s<b.s;
}
map<HuanF,int> book;//记录有没有出现过
map<HuanF,int> dis;//记录最小值
void init()
{
int times=0;
HuanF now;
now.s="123456789";
now.step=0;
queue<HuanF> mmp;
mmp.push(now);
book[now]=1;
dis[now]=0;
while(!mmp.empty())
{
HuanF mm=mmp.front();
mmp.pop();
for(int i=0;i<3;++i)
{
HuanF Net=mm.Colu_down(i);
if(!book[Net])
{
dis[Net]=mm.step+1;
Net.step=mm.step+1;
book[Net]=1;
mmp.push(Net);
}
Net=mm.Row_right(i);
if(!book[Net])
{
dis[Net]=mm.step+1;
book[Net]=1;
Net.step=mm.step+1;
mmp.push(Net);
}
}
}
}
int Hash[10];//将魔方中的数字 分别对应 1 2 3 4 5
int main()
{
int t;
init();
scanf("%d",&t);
char ss[10];
char zz[10];
while(t--)
{
for(int i=0;i<3;++i)
{
scanf("%s",ss+(i*3));
}
for(int i=0;i<9;++i)
{
Hash[ss[i]-'0']=i+1;
}
for(int i=0;i<3;++i)
{
scanf("%s",zz+(i*3));
}
for(int i=0;i<9;++i)
{
zz[i]=Hash[zz[i]-'0']+'0';
}
zz[9]=' ';
HuanF ret;
ret.s=string(zz);
if(ret.s!="123456789"&&dis[ret]==0)
{
cout<<"impossible"<<endl;
}
else
cout<<dis[ret]<<endl;
}
return 0;
}