程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> yzoi2226最小步數的詳細解法,yzoi2226最小解法

yzoi2226最小步數的詳細解法,yzoi2226最小解法

編輯:C++入門知識

yzoi2226最小步數的詳細解法,yzoi2226最小解法


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 }

  最後,歡迎大家的指教。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved