試題 歷屆試題 九宮重排
阿新 • • 發佈:2021-02-11
問題描述
如下面第一個圖的九宮格中,放著 1~8 的數字卡片,還有一個格子空著。與空格子相鄰的格子中的卡片可以移動到空格中。經過若干次移動,可以形成第二個圖所示的局面。
我們把第一個圖的局面記為:12345678.
把第二個圖的局面記為:123.46758
顯然是按從上到下,從左到右的順序記錄數字,空格記為句點。
本題目的任務是已知九宮的初態和終態,求最少經過多少步的移動可以到達。如果無論多少步都無法到達,則輸出-1。
輸入格式
輸入第一行包含九宮的初態,第二行包含九宮的終態。
輸出格式
輸出最少的步數,如果不存在方案,則輸出-1。
12345678.
123.46758
樣例輸出
3
樣例輸入
13524678.
46758123.
樣例輸出
22
解題思路:
題目給出開始狀態和結束狀態,問你從開始狀態到結束狀態最少要走多少步。這種狀態轉換深度無限而寬度有限,而且問的是最短路,因此這裡用BFS是最好的選擇。
不過這裡注意一點,當判斷每種狀態是否相等的時候,如果不用康拓展開把所有排列對映成值,而是自己判斷每一組數的話,會非常耗時,造成程式超時,只能拿40分。因為9個數的全排列大概有30萬種情況,因此我們需要用一下康拓展開(這裡自行百度)。
下面看程式碼
#include <iostream>
#include <stdio.h>
using namespace std;
#include <algorithm>
#include <string.h>
#include <queue>
class Status//建立一個狀態類
{ public:
char a[10]; //把每個數放在一維數組裡儲存
int step;//要走的步數
int pos;//小數點位置
};
queue<Status> q;//定義佇列
int book[400000];//儲存出現過的狀態 共9的階乘個
Status start,end;//開始狀態和結束狀態
int count1= 0;
int jiecheng[10]={1,1,2,6,24,120,720,5040,40320}; //1-9對應的階乘
int kangtuo(Status D)//康拓展開 返回排列對應的對映值
{ int sum1=0;
int i,j;
for(i=1;i<=9;i++)
{ int t=0;
for(j=i+1;j<=9;j++)
if(D.a[i]>D.a[j])
t++;
sum1+=t*jiecheng[9-i];
}
return sum1+1;
}
int BFS()
{ int i,next[4]={1,-1,3,-3};//要加的下一步陣列 右 左 下 上
while(!q.empty())
{
Status D,N;//定義當前狀態和下一個狀態
D=q.front();//取出首元素
q.pop();
if(kangtuo(D)==kangtuo(end)) return D.step;//如果狀態相等
for(i=0;i<4;i++)
{ N=D;
N.pos=D.pos+next[i];//小數點下一個可能的位置
N.step++;
if(next[i]==-1&&(D.pos==1||D.pos==4||D.pos==7)) continue;//越界情況處理
if(next[i]==1&&(D.pos==3||D.pos==6||D.pos==9)) continue;
if(N.pos>9||N.pos<1) continue;
swap(N.a[N.pos],N.a[D.pos]); //交換小數點位置的值和它的要換位置的值
if(!book[kangtuo(N)])//判斷這個狀態是否出現過
{ book[kangtuo(N)]=1;
q.push(N); //如果沒出現過入佇列
}
}
}
return -1;
}
int main()
{ int i,j;
for(i=1;i<10;i++) //輸入開始的狀態
{cin>>start.a[i];
if(start.a[i]=='.')
start.pos=i;
}
start.step=0;
q.push(start);
for(i=1;i<10;i++)//輸入結束的狀態
{ cin>>end.a[i];
if(end.a[i]=='.')
end.pos=i;
}
cout<<BFS()<<endl;
return 0;
}