Description - 問題描述
在各種棋中,棋子的走法總是一定的,如中國象棋中馬走“日”。有一位小學生就想如果馬能有兩種走法將增加其趣味性,因此,他規定馬既能按“日”走,也能如象一樣走“田”字。他的同桌平時喜歡下圍棋,知道這件事後覺得很有趣,就想試一試,在一個(100*100)的圍棋盤上任選兩點A、B,A點放上黑子,B點放上白子,代表兩匹馬。棋子可以按“日”字走,也可以按“田”字走,倆人一個走黑馬,一個走白馬。誰用最少的步數走到左上角坐標為(1,1)的點時,誰獲勝。現在他請你幫忙,給你A、B兩點的坐標,想知道兩個位置到(1,1)點可能的最少步數。
Input - 輸入數據
兩行:第一行A點的坐標,第二行B點的坐標,坐標都是不超過100的自然數。
Output - 輸出數據
兩行,第一行A點走到(1,1)位置的最少步數;第二行是B點走到(1,1)位置的最少步數。
詳細解法
讀完題目我們可以知道,這裡的馬可以走“日”字,亦可以走“田”字,那麼我們假設該點的坐標為(x,y),那麼其可以到達的坐標便有12種情況,分別為:走“日”字:(x-2,y-1),(x-1,y-2),(x-2,y+1),(x-1,y+2),(x+2,y-1),(x+1,y-2),(x+1,y+2),(x+2,y+1);走“田”字:(x-2,y-2),(x-2,y+2),(x+2,y+2),(x+2,y-2)。這樣分析下來,我們便可將所有x的增量以及其所對應的y的增量保存到兩個數組dx[],dy[]中,每次通過對數組對應元素的選擇來完成移動。代碼如下:
int dx[12]={-2,-2,-1,1,2,2,2,2,1,-1,-2,-2};
int dy[12]={-1,-2,-2,-2,-2,-1,1,2,2,2,2,1};
其後,題目要求是分別有兩顆棋子進行移動,其最終目的地都是(1,1),那麼,我們需要對每個棋子的移動方式進行探討嗎?其實不然,對於每一顆棋子,雖然他們的出發點不同,但其終點都為(1,1)。以是,我們便可看成是有一顆棋子從(1,1)出發,分別移動到(x1,y1),(x2,y2)的步數。這樣一來,便可以通過bfs(百度定義:寬度優先搜索算法(又稱廣度優先搜索)是最簡便的圖的搜索算法之一,這一算法也是很多重要的圖的算法的原型。Dijkstra單源最短路徑算法和Prim最小生成樹算法都采用了和寬度優先搜索類似的思想。其別名又叫BFS,屬於一種盲目搜尋法,目的是系統地展開並檢查圖中的所有節點,以找尋結果。換句話說,它並不考慮結果的可能位置,徹底地搜索整張圖,直到找到結果為止。)來完成整個的搜索最少步數的工作了。
既然確定了bfs的算法,那麼我們就需要使用隊列的結構了,我們可以用STL裡的queue來定義一個隊列數組,代碼如下:
#include<queue> queue<int>q[4];
其中,q[1]存儲從(1,1)可到達點的橫坐標,q[2]存儲從(1,1)可到達點的縱坐標,q[3]則表示到達該點所需要的最短步數。假如你說用不慣STL裡的queue,那麼我們也可以自己寫隊列,如下所示:
int que[maxn][4]={0};
同時也需要定義一個頭指針與一個尾指針,其二者的初值都為1,以表示隊列為空,代碼如下:
int head=1,tail=1;
其次,為了最後輸出的方便,我們還要定義一個s[maxn][maxn]數組來存儲到達s[x][y]的最小步數,最後只需要輸出s[x1][y1],s[x2][y2]的值即可。同時,也得將s[1][1]賦值為0,其他元素賦值為1,代碼如下:
memset(s,-1,sizeof(s)); s[1][1]=0;
(接下來,我們全部用自己寫的隊列,進行操作,使用STL代碼將在最後給出)
一開始,我們將初始狀態入隊,即q[1][1]=1,q[1][2]=1,q[1][3]=0,代碼如下:
q[1][1]=1;
q[1][2]=1;
q[1][3]=0;
接著,我們便使用一個循環進行拓展的操作,和1777的倒水一樣,循環成立的條件是head<=tail(即隊列不為空).然後,對於前面所列出的12種情況,我們每一種都要進行探討,即使用一個for循環,從0枚舉到11.其步驟大致如下:①取隊首元素所能到達的位置②判斷當前位置是否越界③判斷當前位置是否到達過(根據bfs的原理先前到達過的步數一定小於當前的步數,故不需要再繼續拓展)④將當前的s[x][y]的值更新,即其值就等於當前q[head][3]+1⑤將新得到的x,y的值入隊⑥判斷是否已將(x1,y1),(x2,y2)的最少步數都搜索到,即判斷s[x1][y1],s[x2][y2]的值是否都>0,是的話就輸出後結束程序否則就彈出隊首元素(前面只是取出,並沒有彈出)並重復①。其代碼如下:
1 while(head<=tail)
2 {
3 for(int d=0;d<12;d++)
4 {
5 int x=q[head][1]+dx[d]; //取隊首元素所能到達橫坐標的位置
6 int y=q[head][2]+dy[d]; //取隊首元素所能到達縱坐標的位置
7 if(x>0&&y>0&&x<=100&&y<=100) //判斷是否在界內
8 if(s[x][y]==-1) //判斷是否已經拓展過
9 {
10 s[x][y]=q[head][3]+1; //更新s[x][y]的值
11 tail++; //將新狀態入隊
12 q[tail][1]=x;
13 q[tail][2]=y;
14 q[tail][3]=s[x][y];
15 if(s[x1][y1]>0&&s[x2][y2]>0) //判斷是否達到了目標狀態
16 {
17 cout<<s[x1][y1]<<endl; //輸出
18 cout<<s[x2][y2]<<endl;
19 return 0;
20 }
21 }
22 }
23 head++; //彈出隊首元素
24 }
最後,給出OJ上ac的代碼,僅供參考:
以下下為自己寫的隊列:
1 #include<iostream>
2 #include<cstring>
3 using namespace std;
4 int const maxn=10000+1;
5 int const area=100+1;
6 int dx[12]={-2,-2,-1,1,2,2,2,2,1,-1,-2,-2};
7 int dy[12]={-1,-2,-2,-2,-2,-1,1,2,2,2,2,1};
8 int s[area][area],q[maxn][4]={0};
9 int x1,y1,x2,y2;
10 int head=1,tail=1;
11 int main()
12 {
13 memset(s,0xff,sizeof(s));
14 cin>>x1>>y1>>x2>>y2;
15 q[1][1]=1;
16 q[1][2]=1;
17 q[1][3]=0;
18 while(head<=tail)
19 {
20 for(int d=0;d<12;d++)
21 {
22 int x=q[head][1]+dx[d]; //取隊首元素所能到達橫坐標的位置
23 int y=q[head][2]+dy[d]; //取隊首元素所能到達縱坐標的位置
24 if(x>0&&y>0&&x<=100&&y<=100) //判斷是否在界內
25 if(s[x][y]==-1) //判斷是否已經拓展過
26 {
27 s[x][y]=q[head][3]+1; //更新s[x][y]的值
28 tail++; //將新狀態入隊
29 q[tail][1]=x;
30 q[tail][2]=y;
31 q[tail][3]=s[x][y];
32 if(s[x1][y1]>0&&s[x2][y2]>0) //判斷是否達到了目標狀態
33 {
34 cout<<s[x1][y1]<<endl; //輸出
35 cout<<s[x2][y2]<<endl;
36 return 0;
37 }
38 }
39 }
40 head++; //彈出隊首元素
41 }
42 }
以下為直接用STL的隊列,其大致入隊出隊的操作都與前者相似,故不再介紹:
1 /*
2 Name: lwq
3 Copyright:
4 Author:
5 Date: 14-12-14 14:17
6 Description: 2226STL
7 */
8
9 #include<iostream>
10 #include<cstring>
11 #include<queue>
12 using namespace std;
13 int const maxn=10000+1;
14 int const area=100+1;
15 int dx[12]={-2,-2,-1,1,2,2,2,2,1,-1,-2,-2};
16 int dy[12]={-1,-2,-2,-2,-2,-1,1,2,2,2,2,1};
17 int s[area][area];
18 queue<int>q[4];
19 int x1,y1,x2,y2;
20 int main()
21 {
22 memset(s,-1,sizeof(s));
23 cin>>x1>>y1>>x2>>y2;
24 q[1].push(1);
25 q[2].push(1);
26 q[3].push(0);
27 while((!q[1].empty())&&(!q[2].empty())&&(!q[3].empty()))
28 {
29 for(int d=0;d<12;d++)
30 {
31 int x=q[1].front()+dx[d];
32 int y=q[2].front()+dy[d];
33 if(x>0&&y>0&&x<=100&&y<=100)
34 if(s[x][y]==-1)
35 {
36 s[x][y]=q[3].front()+1;
37 q[1].push(x);
38 q[2].push(y);
39 q[3].push(s[x][y]);
40 if(s[x1][y1]>0&&s[x2][y2]>0)
41 {
42 cout<<s[x1][y1]<<endl;
43 cout<<s[x2][y2]<<endl;
44 return 0;
45 }
46 }
47 }
48 q[1].pop();
49 q[2].pop();
50 q[3].pop();
51 }
52 }
最後,歡迎大家的指教。