用贪心算法解决马踏棋盘问题

用贪心算法解决马踏棋盘问题时,主要的思想与用递归的方法解决该问题相同,都是用深度优先搜索,只是在选下一个结点的时候做了贪心算法优化,其思路如下:

从起始点开始,根据“马”的走法,它的下一步的可选择数是有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	",board[i][j]);  
 54             }  
 55             printf("

");  
 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("没有路径可走了
");  
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("没有路径可走了
");  
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                     "
不重复遍历完棋盘中每一格的路径.请重新设置矩阵的大小,矩"
141                     "
阵的大小应满足行数和列数均大于4
",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=
",R);
154         scanf("%d",&i);
155         if(i>R){
156             printf("请输入小于等于%d的数
",R);
157             goto label_1;
158         }
159         label_2:printf("请输入马初始纵坐标(Y<=%d):Y=
",C);
160         scanf("%d",&j);
161         if(j>C){
162             printf("请输入小于等于%d的数
",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
",R,C);
174         printf("采用贪心算法所需的时间为%ld	 ms 
",duration);
175         return 0;
176     }
原文地址:https://www.cnblogs.com/Allen-win/p/7095293.html