題意:
思考過程:
首先觀察這道題的資料範圍不是很大,一共才6個位置,並且每個位置只出現一次。
那麼不考慮合法,只算總狀態的話就是7*6*5*4*3*2*1=720
狀態數很少,啟發我們可以用搜尋!
那麼搜尋是用dfs還是bfs?
bfs有一個特性:從s出發,第一次搜尋到狀態t時所用的步數,肯定是所需的最小操作次數。
因為bfs每次擴充套件時只多走一步,假設第一次到某個點所用步數為c,最優解為c'
如果c'<=c,bfs每次會把能擴充套件的都擴充套件掉,則一定會更早擴充套件到c'而不是c。
這個性質有一個直觀的呈現:圖中藍色的是起點,綠色的是終點,紅色的是障礙物,粉色的是最優解。
而dfs則不一定,有可能存在更優的解法,要全部搜尋完才能找到最優解。
再回頭看這道題,由於只詢問是否可達,不問最優解的話,那dfs或者bfs都可以。
用dfs實現:
實現上也有一些小技巧。
1.讀入的是字串,但是實際上移動時又是在一個2*3的字元陣列上向上下左右移動。
直接分位置討論的話由於資料小也是可行的,不過可以考慮引入方向陣列
dx[4]={0,0,-1,1},dy[4]={1,-1,0,0} //假設當前在的位置是(x,y),擴充時只需要 for(int i=0;i<4;i++){ int tx=x+dx[i],ty=y+dy[i]; }
就可以省去分類討論的麻煩。
2.像這種終點明確,起點很多的詢問,如果操作是可逆的,就可以考慮從終點出發,預處理出所有可達的狀態。
具體地,我們可以從"ABCDE*"出發,dfs出所有能到的狀態,把能到的狀態打標記。
處理詢問時,只需要查詢這個狀態有沒有被搜尋過。
程式碼:
#include<bits/stdc++.h> using namespace std; int dx[4]={0,0,-1,1}; int dy[4]={-1,1,0,0}; char a[3][7]; map<string,int>mp; int cnt=0; string check( ){ string s="";//把陣列再轉回字串,方便用map儲存 for(int i=1;i<=2;i++) for(int j=1;j<=3;j++){ s=s+a[i][j]; } return s; } void dfs(int x,int y){ if(mp[check( )]) return; mp[check( )]=1;//對當前訪問到的狀態打標記,防止之後又被搜尋,時間複雜度將會很大。 for(int i=0;i<4;i++){ int tx=x+dx[i],ty=y+dy[i]; if(tx<=2&&tx>=1&&ty<=3&&ty>=1){//擴充的格子肯定不能越界 swap(a[tx][ty],a[x][y]); dfs(tx,ty); swap(a[tx][ty],a[x][y]);//回溯 } } } void solve(){ a[1][1]='A';a[1][2]='B';a[1][3]='C';//轉化成二維陣列 a[2][1]='D';a[2][2]='E';a[2][3]='*'; dfs(2,3); } int main(){ solve();//預處理出所有終點能到的狀態,只要終點能到,那麼它也可以到終點。 int t;cin>>t; for(int i=1;i<=t;i++){ if(t==1) cout<<1<<endl; else cout<<mp[check()]<<endl; } }
題外話:
1.這道題官方資料出鍋啦,只要輸出1就能過。
2.這道題的背景應該是“八數碼”,“八數碼”是一個求給定狀態到目的狀態的最少操作次數,操作也是移動某個特定格子。