杭电ACM1010解题报告(编程语言Java)

1、题目概述:

该题是一道典型的迷宫类搜索题目,为了达到运行效率的要求,需要采取剪枝的策略。所谓剪枝,就是根据题目的特性,将一些没必要的搜索过程省去,从而提高算法的时间效率。

2、题目分析:

问题:

小狗能否从起点S,经过时间T,恰好到达终点D。

条件:

1、Therefore the doggie had to arrive at the door on exactly the T-th second.(小狗必须在T时刻准时到达出口)

2、In every second, he could move one block to one of the upper, lower, left and right neighboring blocks.(每一秒,小狗可以向上下左右四个方向移动一步)

3、He could not stay at one block for more than one second,(他只能在一个位置上待一秒钟)

4、nor could he move into a visited block.(他也不能再回到已经走过的位置)

3、算法设计:

搜索方式主要有两种,即深搜(DFS,Deep-First-Search)和广搜(BFS,Breadth-First-Search)。显然,改题目适合采用深搜算法。

接下来我们需要设计剪枝策略,以提高算法时间效率。

1、奇偶性剪枝

首先,我们先来解释一下奇偶剪枝。

若有一迷宫,将迷宫的每一个位置用0或1来标记(x+y为偶数时为0,x+y为奇数时为1)

0   1   0   1   0

1   0   1   0   1

0   1   0   1   0

1   0   1   0   1

从上面的例子中,我们观察后发现,每一个位置的值必然与其相邻位置的值相反。也就是说我们要从S位置走到D位置,如果S上的值和D上的值相同,必然经过偶数步。如果S上的值和D上的值相异,必然经过奇数步。

所以,我们设计的奇偶剪枝如下:

如果起点S上的值和终点D上的值相同,时间T为奇数,则直接判断不可达,从而省去了搜索的过程,提高了算法的效率。

如果起点S上的值和重点D上的值相异,时间T为偶数,则直接判断不可达。

2、剩余可走区域小于时间。

即能够走得位置数量比时间少,那么将所有的位置走过之后,剩下的时间将无路可走,所以直接判断不可达,省去搜索过程。

3、越界。

当越界之后,则停止继续搜索。

4、超时。

搜索路径长度超过时间T之后,停止搜索。


4、编程方式:

使用递归方式编写程序,即编写一个递归函数。递归搜索时的普遍做法是:先写出退出条件,然后再写自身递归


5、代码(java描述):

import java.util.Scanner;

public class Main {
	public static char[][] maze = new char[7][7];
	public static boolean[][] visited = new boolean[7][7];
	public static int N, M, T;
	public static int start_i, start_j, end_i, end_j;
	
	/*
	 * 函数参数:1、当前搜索到的位置所在行号;2、当前搜索到的位置所在列号;3、当前的时刻
	 * 函数返回值:能否准时到达门口
	 * 用到的全局变量:maze,N,M,T,end_i, end_j;
	 * 函数结构:先列出出口条件,再进行自身递归调用
	 * */
	public static boolean DFS(int cur_pos_i, int cur_pos_j, int cur_time) {
		//判断是否越界
		if(cur_pos_i < 0 || cur_pos_i >= N || cur_pos_j < 0 || cur_pos_j >= M) {
			return false;
		}
		//判断是否超时
		if(cur_time > T) {
			return false;
		}
		//当前位置是否是墙
		if(maze[cur_pos_i][cur_pos_j] == ‘X‘) {
			return false;
		}
		//当前位置是否已经走过
		if(visited[cur_pos_i][cur_pos_j]) {
			return false;
		}
		//奇偶性剪枝
		if((cur_pos_i + cur_pos_j + end_i + end_j + (T - cur_time)) % 2 == 1) {
			return false;
		}
		//如果已经到达门口
		if(maze[cur_pos_i][cur_pos_j] == ‘D‘) {
			if(cur_time == T) {
				return true;
			}
			else {
				return false;
			}
		}
		//将当前位置标记为已走过
		visited[cur_pos_i][cur_pos_j] = true;
		//递归调用自身
		boolean ok = DFS(cur_pos_i + 1, cur_pos_j, cur_time + 1) || DFS(cur_pos_i - 1, cur_pos_j, cur_time + 1)
				|| DFS(cur_pos_i, cur_pos_j + 1, cur_time + 1) || DFS(cur_pos_i, cur_pos_j - 1, cur_time + 1);
		//将当前位置标记回未走过
		visited[cur_pos_i][cur_pos_j] = false;
		return ok;
	}
	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		while(true) {
			N = cin.nextInt();
			M = cin.nextInt();
			T = cin.nextInt();
			
			if(N == 0 && M == 0 && T == 0) {
				break;
			}
			int blocks = 0;
			for(int i = 0; i < N; i ++) {
				//String str = cin.nextLine();
				String str = cin.next();
				for(int j = 0; j < M; j ++) {
					maze[i][j] = str.charAt(j);
					if(maze[i][j] == ‘S‘) {
						start_i = i;
						start_j = j;
					}
					else if(maze[i][j] == ‘D‘) {
						end_i = i;
						end_j = j;
					}
					else if(maze[i][j] == ‘X‘) {
						blocks ++;
					}
				}
			}
			//剪枝,如果可走的位置数少于时间,则不可能在T时刻到达门口,因为走完所有位置后会无路可走
			if(M * N - blocks < T) {
				System.out.println("NO");
			}
			else {
				//因为DFS函数用到了全局变量visited,所以需要在调用DFS之前,将visited数组初始化
				for(int i = 0; i < N; i ++) {
					for(int j = 0; j < M; j ++) {
						visited[i][j] = false;
					}
				}
				if(DFS(start_i, start_j, 0)) {
					System.out.println("YES");
				}
				else {
					System.out.println("NO");
				}
			}
		}
	}

}


杭电ACM1010解题报告(编程语言Java),布布扣,bubuko.com

杭电ACM1010解题报告(编程语言Java)

上一篇:借助云开发实现小程序订阅消息(模板消息)推送功能


下一篇:微信公共平台开发2 .net