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\n", Sx, Sy);
145 
146 //    printf("Ex = %d Ey = %d\n", Ex, Ey);
147 
148 //    printf("step: 1 :(%d,%d)\n", Sx,Sy);
149 
150 //    printf("step: %d :(%d,%d)\n", 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\n", tmp);
159         
160             tmp = (tmp+turn[i][direction])%4;
161 
162         //    printf(" after :%d\n\n", 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)\n", 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\n",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)\n", 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\n\n",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\n", BFS());
261     }
262     return 0;
263 }
View Code

 

 

 

 

 

 

转载于:https://www.cnblogs.com/inuyasha1027/p/ACM_POJ_3083.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值