用貪心算法解決馬踏棋盤問題


用貪心算法解決馬踏棋盤問題時,主要的思想與用遞歸的方法解決該問題相同,都是用深度優先搜索,只是在選下一個結點的時候做了貪心算法優化,其思路如下:

從起始點開始,根據“馬”的走法,它的下一步的可選擇數是有0—8個的。

已知,當馬下一步的可選擇數為0的時候(即馬沒有下一個節點可跳),進行回溯。當下一步的可選擇數有1個的時候,我們直接取那一步就可以。但是如果下一步的可選擇數有多個的時候呢?

在之前用的遞歸+回溯算法中,我們是任意取一個的,只要它在棋盤內,且未遍歷就可以了。但其實我們怎么選下一步,對搜索的效率影響是非常大的!

 

貪心算法:又稱貪婪算法,是指在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的局部最優解。

用貪心算法的思想來解決在棋盤如何選擇下一個節點的問題,就是計算馬兒的哪個下一節點對應的下一個節點(即下下個節點)的總數最少,就將哪個下一節點視為最優。

例如:假設a、b、c、d為馬兒可以選擇的下一個節點,選哪個比較好,就看哪個點的后續下一步比較少。如果馬走到a點后的下一步有3個選擇;而b的下一步有2個;c有1個,d有2個,那么最優選擇是:c點。

因為c點的后續下一步很少,如果不先遍歷它的話以后可能會很難遍歷到它。甚至極端一點的情況是,如果現在不遍歷它,以后都遍歷不到了。遍歷不成功的時候只能回溯,一直回溯到此刻的點,然后選了c點以后才能完成,這就浪費了大量的時間。

選擇下一個節點的函數如下圖所示,其中,num是馬在當前位置時,下一個節點的數目。數組a[]的長度為num,a[]中存放馬的每個下一節點所對應的下一個節點的數目。

用貪心算法求解馬踏棋盤問題的一個解的代碼如下。當然,一般來講棋盤的大小應該為8*8,此處,定義的是6*6,主要是為了方便和我之前的用遞歸+回溯的方法寫的代碼進行對比,用遞歸的話,用8*8的棋盤,執行的時間有點長,於是就用了6*6的.

  1     # include <stdio.h>  
  2     # include <stdlib.h>  
  3     # include <math.h>  
  4     # include <time.h>
  5     # define R 6  
  6     # define C 6  
  7     int cheesboard [R] [C];  
  8     const int moveX [8] = {-2,-1,1,2,2,1,-1,-2};  
  9     const int moveY [8] = {1,2,2,1,-1,-2,-2,-1};  
 10     //初始化棋盤,將棋盤所有的位置賦值為0  
 11     void initBoard (int board[][C]){  
 12         int i ,j;
 13         for(i = 0; i < R; i ++){  
 14             for( j = 0; j < C; j ++){  
 15                 board[i][j] = 0;  
 16             }  
 17         }  
 18     }
 19     //從待選的下一個點的集合中,選取它們其中所對應的下一個節點數目最少的一個
 20     int getMinPath (int a[],int num){  //num:下一個節點的數目,
 21         //a[]:每一個 下一個節點 對應的下一節點的數目
 22         //定義下標為  
 23         int i = 0,index=0;  
 24         //定義最小的值為a(0),找到最小的值,而且大於0的值  
 25         int min= a[0];  
 26         //找出數組中第一個大於0的值為min
 27         for(i = 0 ; i< num; i++)  
 28         {  
 29             if(a[i] > 0)  
 30             {  
 31                 min = a[i];  
 32                 index = i;  
 33                 break;  
 34             }  
 35         }  
 36         for(i = index + 1; i < num ;  i++)  
 37         {  
 38             if(a[i] > 0 && min > a[i])  
 39             {  
 40                 min = a[i];  
 41                 index = i;  
 42             }  
 43         }  
 44         if(a[index] > 0)  
 45             return index;  
 46         return -1;  
 47     }
 48     // 打印路徑  
 49     void printPath (int board[][C]){  
 50         int i,j;  
 51         for (i = 0; i < R; i++){  
 52             for ( j = 0; j < C; j++){  
 53                 printf("%d\t",board[i][j]);  
 54             }  
 55             printf("\n\n");  
 56         }  
 57     }  
 58     // 獲得馬行走的路徑  
 59     void getPath (int board [][C], int startX, int startY){  
 60         //下一個可行位置的數目
 61         int next = 0;  
 62         //路徑最短的可行位置在數組中的位置  
 63         int min;  
 64         //下一個可行位置的可行位置數目  
 65         int nextNext;  
 66         //將棋盤初始化  
 67         initBoard (board);  
 68         // 存放下一個位置對應的下一個位置的數目  
 69         int nextNum[8] = {0,0,0,0,0,0,0,0};  
 70         //下一個位置的在二維數組中對應位置,初始為0  
 71         int nextX[C] = {0};  
 72         int nextY[R] = {0};  
 73         //第一個位置賦值為1  
 74         board [startX] [startY] = 1;  
 75         int m,i,j;  
 76         //走完所有的點要循環63次  
 77         for ( m = 1; m < R*C; m++){  
 78                 //當前點其后面可行的位置設為0  
 79                 next = 0;  
 80         //通過循環來判斷該位置是否還可以向下面位置移動  
 81         for ( i =  0; i < 8; i++){  
 82             if(startX + moveX[i] < 0 || startX + moveX[i] >= R  
 83                 || startY + moveY[i] < 0 || startY + moveY[i] >= C 
 84                 || board[startX+moveX[i]][startY+moveY[i]] != 0){  
 85                     continue;  
 86                 }
 87                 //如果可以向下一個位置移動的話,通過next數組保存下來,通過next記錄下有多少個  
 88             nextX [next] = startX + moveX[i];  
 89             nextY [next] = startY + moveY[i];  
 90             next ++;  
 91         }  
 92         //循環結束之后,對next的值進行判斷,當為1的時候  
 93         if (next == 1){  
 94             //讓min=0,表示現在所需要的位置是在我們的保存next數組中的第一位  
 95             min = 0;  
 96         //設置初始點  
 97             goto set_nextpoint;  
 98         }  
 99         //無法向下一個位置移動了  
100         else if (next == 0){  
101             printf("沒有路徑可走了\n");  
102             goto print_path;  
103         }  
104         else {  
105                 /*當有多個路徑可以走的時候,檢測每一個點還可不可以繼續向下走然后 
106                 記錄下來該點有幾個點可以向下走,找到最少的一個但是不為0的哪一個 
107                 */  
108             for (i = 0; i<next; i++){  
109                 nextNext = 0;  
110                 for(j = 0; j < 8; j++){  
111                     if(nextX[i] + moveX[j] >=0 && nextX[i] + moveX[j] < R  
112                         && nextY[i] + moveY[j] >= 0 && nextY[i] + moveY[j] <C  
113                         && board [nextX[i]+moveX[j]][nextY[i]+moveY[j]] == 0){  
114                             nextNext ++;
115                         }
116                 }  
117                 nextNum[i] = nextNext;  
118             }  
119             if ((min = getMinPath(nextNum,next))>=0 )  
120             {  
121                 goto set_nextpoint;  
122             }  
123             else{  
124                 printf("沒有路徑可走了\n");  
125                 goto print_path;  
126             }  
127         }  
128     set_nextpoint:  
129             startX = nextX[min];  
130             startY = nextY[min];  
131             board[startX][startY] = m+1;  
132         }  
133     print_path:  
134         printPath(board);  
135       
136     }  
137      void judgeExistence(){
138             if(R<=4 || C<=4){//通過已有的理論給出判斷條件
139                 printf("棋盤矩陣為%d * %d 時,馬從其中一些節點出發,不能夠找到"
140                     "\n不重復遍歷完棋盤中每一格的路徑.請重新設置矩陣的大小,矩"
141                     "\n陣的大小應滿足行數和列數均大於4\n",R,C);
142                 exit(0);
143             }
144         }
145     int main (){  
146         int i,j;  
147         //main函數后首先執行一個判斷存在性的函數    
148         judgeExistence();
149         clock_t start, finish;  //計算核心方法一共花費了多少時間
150         long duration;
151 
152         //獲得最開始的位置  
153         label_1:printf("請輸入馬初始橫坐標(X<=%d):X=\n",R);
154         scanf("%d",&i);
155         if(i>R){
156             printf("請輸入小於等於%d的數\n",R);
157             goto label_1;
158         }
159         label_2:printf("請輸入馬初始縱坐標(Y<=%d):Y=\n",C);
160         scanf("%d",&j);
161         if(j>C){
162             printf("請輸入小於等於%d的數\n",C);
163             goto label_2;
164         }
165         start=clock();//開始時間
166         i=i-1;
167         j=j-1;
168     
169         //調用該函數獲取路徑  
170         getPath(cheesboard, i, j); 
171         finish=clock();
172         duration=finish-start;
173         printf("棋盤的大小為%d*%d\n",R,C);
174         printf("采用貪心算法所需的時間為%ld\t ms \n",duration);
175         return 0;
176     }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM