zoukankan      html  css  js  c++  java
  • 用贪心算法解决马踏棋盘问题

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

    从起始点开始,根据“马”的走法,它的下一步的可选择数是有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     }
  • 相关阅读:
    接口测试总结
    Jmeter教程索引贴
    [转] 配置Log4j
    Jmeter报告优化之New XSL stylesheet
    Jmeter默认报告优化
    iOS 自动移除KVO观察者
    iPhone X 适配 ( iOS 11适配 )
    iOS中自动登录的设计
    iOS APP 安全测试
    APP安全测评checklist---Android
  • 原文地址:https://www.cnblogs.com/Allen-win/p/7095293.html
Copyright © 2011-2022 走看看