1. 程式人生 > 其它 >試題 歷屆試題 九宮重排

試題 歷屆試題 九宮重排

技術標籤:藍橋杯暴力列舉演算法演算法資料結構bfsc++

問題描述
  如下面第一個圖的九宮格中,放著 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; }