如果一個 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?'\n':' ');
}
printf("step=%d\n",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]='\0';
HuanF ret;
ret.s=string(zz);
if(ret.s!="123456789"&&dis[ret]==0)
{
cout<<"impossible"<<endl;
}
else
cout<<dis[ret]<<endl;
}
return 0;
}