zoukankan      html  css  js  c++  java
  • POJ3083

    题目连接:http://poj.org/problem?id=3083

    题目大意:这是一道有基于图搜索的题目,主要考察两个方面,

    一个是DFS搜索图中左优先、右优先的相关路径。

    另一个考察点是BFS用于搜索图中最短的路径。

    解题报告:

    大致的算法步骤如下:

     使用DFS思想来解决题目中的左优先、右优先步数计算

    使用BFS思想来解决题目中的最短路径步数的计算。

    根据本题的所要实现的是在图中的某一点,向四个方向有计划的进行迈进,

    所以在表示图的数据结构上,采用矩阵方法所对应的二维数组来表示在时间空间效率上是较优的。 

    1.InitGraph()

    对于图对应的字符串的读取处理的方法大致分为两种,

    一个是开辟一个最大限度的40 * 40的二维字符串数组,

    在嵌套分别对应行和列的两个循环后,

    将图对应的迷宫字符矩阵读入到数组中。

    然后在一个个的读取判断并且记录哪一个是S、E等等。

    另一个方法就是只申请一个40的一维字符串数组,

    外面一个针对于迷宫字符串h的大循环,

    每次读取一行。

    然后针对改行进行一个针对于w的小循环,

    然后一一记录S,E所在的坐标。

    1,如果读入的是E,记录E的坐标Ex Ey,将map[Ex][Ey]置为2

    2.如果读入的是S,记录S的坐标Sx Sy,并且将map[Sx][Sy]的值置为-1

    3.如果读入的是'#',将对应其坐标的map二维数组值置为 -1

    4.如果读入的是'.',将对应其坐标的map二维数组值置为0

    对于当前坐标(x,y)在相应的h*w的矩阵中,x对应的是h,而 y对应的是w。

    这个是根据个人喜好而定的,不是绝对的。

     1 void InitGraph()
     2 {
     3     int i,j;
     4         
     5 
     6         
     7     for(i = 0; i < h; i++)
     8     {
     9         scanf("%s", line);
    10         getchar();
    11 
    12         for(j = 0 ; j < w; j++)
    13         {
    14              
    15                 
    16             if(line[j]=='#')
    17             {
    18                 map[i][j] = -1;
    19             }
    20                             
    21             else if(line[j]=='S')
    22             {
    23                 Sx = i;
    24                 Sy = j;
    25                 map[i][j] = -1;
    26             }
    27             else if(line[j]=='E')
    28             {
    29                 Ex = i;
    30                 Ey = j;
    31                 map[i][j] = 2;
    32             }
    33 
    34             else if(line[j]=='.')
    35             {
    36                 map[i][j] = 0;
    37             }
    38         }
    39 
    40     }
    41 
    42     if(Sx == 0)//on the 0 direction should be 2
    43     {
    44         d = 2;
    45         
    46 
    47     }
    48 
    49     else if(Sx == h -1) //on the 2 direction should be 0
    50     {
    51         d = 0;
    52     
    53     }
    54     
    55     else if(Sy == 0) // on the 1 , direction should be 3
    56     {
    57         d = 3;            
    58         
    59             
    60 
    61     }
    62         
    63     else if(Sy == w-1)//on the 3 , direction should be 1
    64     {
    65         d = 1;
    66         
    67     }
    68 
    69 
    70 
    71     return;
    72 }

    因为根据题目的要求,Output的前两个步骤分别是针对左转优先和右转优先的,

    所以,在S的起始位置就应该确定好初始的方向。

    根据S的所在位置设定它的前后左右的方向。

    如下图所示:

    根据题意可以知道,S的位置共分为一下几种情况:

    a.

    S在长方形的最下边上:

    此时设定方向为0

    b.

    S在长方形的最右边上:

    此时设定方向为1

    c.

    S在长方形的最左边上:

    此时设定方向为3

    d.

    S在长方形的最上边上:

    此时设定方向为2

    所设定的方向也就是根据当前的左(右)转优先所要前进的方向。

    2.DFS()

    在这里需要说明一下,其实针对本题的DFS算法的实现大致有一下几种:

    a.根据左优先还是右优先不同的方式分别针对两段实现代码。

    b.使用相应的标识符来标识当前是左优先还是右优先,然后在实现方法中在仔细进行判断。

    c.根据左优先还是右优先的实现方式来确定两个direction的不同设定,

    根据上述(a b c d四种情形的方向设定)

    的规律可以总结出,如果设当前方向为D并且是左转有限的话,

    那么它的旋转方向一定是这样的:

    先左转方向为1

    如果不可以前进,那么接下来选取的方向为0,

    如果依旧不能行进,接下来的方向选取的是3;

    所以如果是左转优先的话,

    假设当前结点是0

    对四个方向的访问顺序是:

    首先左转90度 此时方向为1(如果行不通)->再右转90度 此时方向为0(如果行不通)

    ->再右转90度 此时方向为3 (如果行不通)->再右转90度 此时方向为2(这个时候刚刚好就是原路返回了)

    同样的情况如果是右转优先的话,

    假设当前结点是0

    对四个方向的访问顺序是:

    首先右转90度,然后依次左转90度,如果不满足在左转90度,

    如果还是不满足继续左转90度,如果仍旧不满足在此左转90度,

    同样的在最后这次左转90度的时候,结点是按照原路返回的。

    同样根据方向事先是定义好的,如上图所示:

    上(0)下(2)左(1)右(1)

    对于当前的点的方向为D的话,

    左转为(D+1)%4

    右转为(D+3)%4

    所以左转优先对应(D+i)%4的话呢,

    i的值分别是 1 3 3 3

    右优先的话呢,对应的是同样的公式:

    (D+i)%4

    i的值分别对应的是 3 1 1 1

    所以可以定义一个数组turn[2][2]={{1,3},{3,1},{3,1},{3,1}}

    所以如果让一个结点满足左转优先的条件的话,

    可以设置一个for循环配合一个,

    设定数组的原因其实就是LZ一厢情愿的想要把左右优先对应的两种方法合并为一组代码实现。

     1 declare global variable 
     2  turn[2][2] ={{1,3},{3,1},{3,1},{3,1}}
     3 
     4 if left set turn = 0
     5 if right set turn =1
     6 
     7 for(i = 0 ; i < 4; i++)
     8 {
     9    D = (D+turn[i][turn])%4      
    10 }


    方向选取的问题解决了之后呢,还有一点需要注意一下,

    那就是坐标前后移动的问题,

    设定当先坐标为(x,y)

    如果想向方向X(X取值为 0,1,2,3 中其中一个)移动的话,

    则需要对x,y 坐标进行相加减才可以得出。

     

    所以在每次知道要行进的方向之后,只要让当前坐标对应的加上对应该点坐标的值

    就可以实现向该方向行进一个单位了。

    所以可以实现设定好数组direction [4][2]={{-1.,0},{0,-1},{1,0},{0,1}}

    其中4 分别对应的是四个行进方向(0,1,2,3)

    2对应的是x,y坐标的加减1或是0对应的值的变化

    例如当前所标的方向为D,坐标为(x,y)我想要向D方向前进一个单位的话,

    可以使用以下代码实现:

    nextX = x+direction[D][0];

    nextY = y +direction[D][1];

    而当前的方向是根据外层的一个 4次的循环来实现的,也就是上面的代码,

    需要根据DFS方法传进的参数来决定是左转优先(DFS(0))

    还是右转优先(DFS(1))来判断当前所选取的方向。

    如是下来就可以实现DFS这一方法了,具体代码如下:

     1 int DFS(int direction)
     2 {
     3     
     4 
     5     int s=2;
     6     //this is used to count the steps
     7 
     8     int tmpx,tmpy;
     9 
    10     int i;
    11     int tmp;
    12 
    13     tmp =d;
    //d is already set in the InitGraph method
    15 17 switch(d) 18 { 19 case 0: 20 { 21 x = Sx-1; 22 y = Sy; 23 break; 24 } 25 case 1: 26 { 27 x = Sx; 28 y = Sy-1; 29 break; 30 } 31 case 2: 32 { 33 x = Sx+1; 34 y = Sy; 35 break; 36 } 37 case 3: 38 { 39 x = Sx; 40 y = Sy+1; 41 break; 42 } 43 } 44 45 //上述的switch case 是根据具体的方向向前迈进一步进入到迷宫中去,
       //这样可以免去在'S'点出就四周搜索的情况,故记录步数的变量已经置为2 46 while(map[x][y]==0) 47 { 48 49 for(i = 0; i < 4; i++) 50 { 51 tmp = (tmp+turn[i][direction])%4; 52 53 tmpx = x+dir[tmp][0];      //试探性的向该方向进行迈进,先设为tmpx/tmpy 如果tmpx tmpy
         //满足迈进条件的话,在将其赋值为x,y
    54 tmpy = y+dir[tmp][1]; 55 56 if(tmpx >=0 && tmpx< h && tmpy>=0 && tmpy< w && map[tmpx][tmpy]>=0 ) 57 { 58 x=tmpx; 59 y=tmpy; 60 61 s++; 62 63 break; 64 65 66 } 67 68 69 } 70 71 72 73 }//while 74 75 return s; 76 77 78 }

     3.BFS()

    这个方法主要是用于求取图中的'S'和'E'之间的最短路径的最短步数的,

    其实就是普通的BFS实现就可以了,但是为了方便起见,可以自己简单的写一个

    queue来替代模板函数的。

    许多人写了一个Point的结构体其中包含了当前节点的坐标x,y 以及上从S结点到当前结点所需要的步数的记载,

    又为了很好地模拟BFS基本实现方法创建了一个vis的数组用于标示图中的某一个结点是否被访问过。

    LZ觉得这样比较麻烦的,

    LZ是这样想的,最短路径是在最后求取的,即Sample Out 最后输出的数值,

    这样的话,就可以借助于修改map这个二维数组本身来实现它的vis的功能

    而且同样也可以使用map[][]对应的二维数组的x,y结点所确定下来的数值来记录从S点到当前结点所走的步数的。

    而且可以仅仅使用一个q这一个int来实现记录当前数组的(x,y)坐标的值。

    这样的话,是要声明一个queue<int>Q;每次将q进行压入队列的话,就可以实现将一个坐标点(x,y)进行压入队列了。

    即,在初始化的时候,应该将Sx, Sy 也就是S在map数组中的坐标点(x,y)

    首先压入到队列中:

    int q = Sx*(h-1)+Sy;

    Q.push(q);

    然后在出队的时候:

    q = Q.front();

    Sx = q/(h-1);

    Sy = q%(h-1);

    这样的话节省了很多的空间,又因为Sx的范围是[0,h-1)

    所以q的值最大也会被限定在h*h的范围内,而h*h的是一定是小于 40*40=1600的,所以q设定为int即可。

    然后实现map二维数组的重复使用,当然这会改了原图,也就是使得图所对应的二维数组与初始化图的时候有所不同。

    但是,BFS是最后一个调用的方法,所以即便图被修改了也不会影响到前面两个方法的调用的。 

    接下来是我的实现代码:

    map在初始化的时候已经设定定好了,

    在这个方法里面又将map[Ex][Ey]=0

    所以map[x][y] =0 则代表(x,y)对应的结点未被访问过。

    反之,则被访问过或是不存在路径

    又根据BFS的访问规则:一个结点不能重复被访问,所以一个结点(x,y)在被访问的时候,

    将map[x][y]的数值置为S到该点的步数了,那么它将不为0,

    1.map[x][y]>0代表的是该结点(x,y)已经访问过

    2.map[x][y]=0代表的是(x,y)未被访问过

    3.map[x][y]<0代表的是(x,y)这一坐标在途中是'#'是不可走的。

    所以最终map[Ex][Ey]中的数值对应的是S到E之间所走过的最短路径的数值

     1 int BFS()
     2 {
     3     
     4     int tmpx,tmpy;
     5 
     6 
     7     int p;
     8     int tmp;
     9     queue<int>Q;
    10     
    11     map[Ex][Ey] = 0;
    12     map [Sx][Sy] = 1;
    13 
    14 
    15   p = Sx*(h-1) +Sy;
    16   Q.push(p);
    17  
    18   while(map[Ex][Ey]==0 )19   {
    20       p = Q.front();
    21       Q.pop();
    22       
    23       for(int i = 0; i < 4; i++)
    24       {
    25           
    26           tmpx = p/(h-1)+dir[i][0];
    27           tmpy = p%(h-1)+dir[i][1];
    28                    
    29             if(map[tmpx][tmpy]==0&&tmpx>=0 && tmpx < h && tmpy >= 0 && tmpy <w)
              //if the value of map[x][y]=0 ,it presents that map is the first visited 30 { 31 map[tmpx][tmpy] = map[p/(h-1)][p%(h-1)]+1; 32 //this is use to counter the steps 33 //and after it plus 1 , map[][] is visited 34 35 36 if(tmpx==Ex&&tmpy==Ey) 37 return map[Ex][Ey]; 38 39 tmp = tmpx*(h-1) + tmpy; 40 Q.push(tmp); 41 42 }//if 43 } 44 } 45 46 47 48 }

     下面折叠的代码是整个程序的实现代码:

    以及在程序调试的时候注释掉的用于打印结点行走轨迹的代码:

      1 #include<stdio.h>
      2 #include<queue>
      3 
      4 using namespace std;
      5 
      6 int DFS(int);
      7 int BFS();
      8 void  InitGraph();
      9 
     10 int map[42][42];
     11 
     12 char line[42];
     13 
     14 int dir[4][2] ={{-1,0},{0,-1},{1,0},{0,1}};
     15 
     16 int turn[4][2]={{1,3},{3,1},{3,1},{3,1}};
     17 
     18 int Sx,Sy,Ex,Ey;
     19 int h,w;
     20 
     21 int x,y;
     22 
     23 int d;
     24 
     25 void InitGraph()
     26 {
     27     int i,j;
     28         
     29 
     30         
     31     for(i = 0; i < h; i++)
     32     {
     33         scanf("%s", line);
     34         getchar();
     35 
     36         for(j = 0 ; j < w; j++)
     37         {
     38              
     39                 
     40             if(line[j]=='#')
     41             {
     42                 map[i][j] = -1;
     43             }
     44                             
     45             else if(line[j]=='S')
     46             {
     47                 Sx = i;
     48                 Sy = j;
     49                 map[i][j] = -1;
     50             }
     51             else if(line[j]=='E')
     52             {
     53                 Ex = i;
     54                 Ey = j;
     55                 map[i][j] = 2;
     56             }
     57 
     58             else if(line[j]=='.')
     59             {
     60                 map[i][j] = 0;
     61             }
     62         }
     63 
     64     }
     65 
     66     if(Sx == 0)//on the 0 direction should be 2
     67     {
     68         d = 2;
     69         
     70 
     71     }
     72 
     73     else if(Sx == h -1) //on the 2 direction should be 0
     74     {
     75         d = 0;
     76     
     77     }
     78     
     79     else if(Sy == 0) // on the 1 , direction should be 3
     80     {
     81         d = 3;            
     82         
     83             
     84 
     85     }
     86         
     87     else if(Sy == w-1)//on the 3 , direction should be 1
     88     {
     89         d = 1;
     90         
     91     }
     92 
     93 
     94 
     95     return;
     96 }
     97 
     98 
     99 int DFS(int direction)
    100 {
    101     
    102 
    103     int s=2;
    104     //this is used to counter the steps
    105 
    106     int tmpx,tmpy;
    107 
    108     int i;
    109     int tmp;
    110 
    111     tmp =d;
    112 
    113 
    114 
    115     switch(d)
    116     {
    117     case 0:
    118         {
    119             x = Sx-1;
    120             y = Sy;
    121             break;
    122         }
    123     case 1:
    124         {
    125             x = Sx;
    126             y = Sy-1;
    127             break;
    128         }
    129     case 2:
    130         {
    131             x = Sx+1;
    132             y = Sy;
    133             break;
    134         }
    135     case 3:
    136         {
    137             x = Sx;
    138             y = Sy+1;
    139             break;
    140         }
    141     }
    142 
    143 
    144 //    printf("Sx =%d  Sy =%d
    ", Sx, Sy);
    145 
    146 //    printf("Ex = %d Ey = %d
    ", Ex, Ey);
    147 
    148 //    printf("step: 1 :(%d,%d)
    ", Sx,Sy);
    149 
    150 //    printf("step: %d :(%d,%d)
    ", s,x,y);
    151 
    152         
    153     while(map[x][y]==0)
    154     {
    155     
    156         for(i = 0; i < 4; i++)
    157         {
    158         //    printf("before : %d
    ", tmp);
    159         
    160             tmp = (tmp+turn[i][direction])%4;
    161 
    162         //    printf(" after :%d
    
    ", tmp);
    163                 
    164             tmpx = x+dir[tmp][0];
    165             tmpy = y+dir[tmp][1];
    166                 
    167             if(tmpx >=0 && tmpx< h && tmpy>=0 && tmpy< w && map[tmpx][tmpy]>=0 )
    168             {
    169                 x=tmpx;
    170                 y=tmpy;
    171             
    172                 s++;
    173 
    174             //    printf("step: %d :(%d,%d)
    ", s,x,y);
    175                 
    176                 
    177                 break;
    178 
    179                 
    180             }
    181 
    182             
    183         }
    184 
    185         
    186 
    187     }//while
    188 
    189     return s;
    190         
    191 
    192 }
    193 
    194 int BFS()
    195 {
    196     
    197     int tmpx,tmpy;
    198 
    199 
    200     int p;
    201     int tmp;
    202     queue<int>Q;
    203     
    204     map[Ex][Ey] = 0;
    205     map [Sx][Sy] = 1;
    206 
    207 
    208   p = Sx*(h-1) +Sy;
    209   Q.push(p);
    210  
    211   while(map[Ex][Ey]==0 ||!Q.empty())
    212   {
    213       p = Q.front();
    214       Q.pop();
    215       
    216       for(int i = 0; i < 4; i++)
    217       {
    218           
    219           tmpx = p/(h-1)+dir[i][0];
    220           tmpy = p%(h-1)+dir[i][1];
    221          // printf("i: %d
    ",i);
    222             
    223             if(map[tmpx][tmpy]==0&&tmpx>=0 && tmpx < h && tmpy >= 0 && tmpy <w)
    224 //if the value of map[x][y]=0 ,it presents that map is the first visited
    225             {
    226                  map[tmpx][tmpy] = map[p/(h-1)][p%(h-1)]+1;
    227                 //this is use to counter the steps
    228                 //and after it plus 1 , map[][] is visited
    229 
    230             //    printf("before step: (%d,%d)
    ", p/(h-1), p%(h-1));
    231                  
    232                 if(tmpx==Ex&&tmpy==Ey)
    233                     return map[Ex][Ey];
    234 
    235                 tmp = tmpx*(h-1) + tmpy;
    236                 Q.push(tmp);
    237 
    238             //    printf("step : %d  (%d ,%d) push in queue
    
    ",map[tmpx][tmpy], tmpx, tmpy);
    239 
    240                         
    241             }//if
    242       }
    243   }
    244 
    245  
    246      
    247 }
    248 
    249 int main()
    250 {
    251     int n;
    252 
    253     scanf("%d",&n);
    254     while(n--)
    255     {
    256     scanf("%d%d", &w, &h);
    257     InitGraph();
    258 
    259     printf("%d %d",DFS(0),DFS(1));
    260     printf(" %d
    ", BFS());
    261     }
    262     return 0;
    263 }
    View Code
  • 相关阅读:
    linux 安装软件三种方法
    megalo -- 网易考拉小程序解决方案
    层叠上下文 Stacking Context
    关于document.write
    学习块格式化上下文(BlockFormattingContext)
    jQuery 源码分析 8: 回头看jQuery的构造器(jQuery.fn,jQury.prototype,jQuery.fn.init.prototype的分析)
    简化版的Flappy Bird开发过程(不使用第三方框架)
    jQuery 源码分析 7: sizzle
    jQuery 源码分析6: jQuery 基本静态方法(二)
    jQuery 源码分析5: jQuery 基本静态方法(一)
  • 原文地址:https://www.cnblogs.com/inuyasha1027/p/ACM_POJ_3083.html
Copyright © 2011-2022 走看看