【问题描述】
有一个 n×m 的网格,其中包含一些实心单元和一些空心单元。网格左上角的坐标为(1, 1),而右下角的坐标为(n, m)。其中有 k 个实心单元,而其他的则是空心的。这时从坐标为( x(s),y(s)( ))的单元中心向四个对角方向之一(也就是东北、西北、东南和西南)的方向发射一个激光束,如果激光束遇到实心单元或网格边缘则形成反射或折射,方式如下:
一段时间后,激光束将进入一个死循环,计算在进入死循环之前激光束穿越至少一次的空单元格总数,穿越是指穿过单元中心。
【输入形式】
输入的第一行包含三个整数 n、m 和 k (1≤n、m≤1000, 0≤k≤1000)。接下来的 k 行每行包含两个整数 x(i)( )和 y(i)( )(1≤x(i)≤n,1≤y(i)≤m),表示第 i 个实心单元的位置。
最后一行包含两个整数x(s)( ) y(s)( )(1≤x(s)≤n,1≤y(s)≤m)以及激光发射方向,分别用"NE"、"NW"、"SE"、"SW"代表东北、西北、东南、西南方向。
【输出形式】
输出仅有一行一个数字,表示激光束进入死循环之前所穿越过至少一次的空心单元格的总数。
【样例输入1】
3 3 0
1 2 SW
【样例输出1】
6
【样例输入2】
7 5 3
3 3
4 3
5 3
2 1 SE
【样例输出2】
14
【提示】
可以将 n×m 的网格扩大为(n+2)×(m+2),其中的所有:
(0, i), i=0,1,...,m+1单元
(j, 0),j=0,1, ..., n+1单元
(n+1, i), i=0,1,...,m+1单元
(j, m+1), j=0, 1,...,n+1单元
都可以看做为实心单元。
【评分标准】274E
解题思路
-
全局初始化:gridState[1002][1002]网格空心、实心状态、visitedDirctions [1002][1002]激光访问过的单元格和方向、total死循环前穿越过的空心单元格总数
-
命令行读取:n、m、k
-
main函数变量初始化:x、y激光当前所在的单元格坐标,dx、dy激光移动的方向,dx激光在行上移动的增量,dy在列反向移动的增量、激光方向dir
-
初始化网络边界为实心,行:[0][i]、[n+1][i]、[i][0]、[i][m+1]都设置为1
-
设置实心单元格位置gridState[x][y]=1
-
命令行读取激光初始位置x、y、激光初始方向dir
-
分为四种情况,读入的激光初始方向dir、设置dx、dy
-
开始模拟激光,调用laser方法laser(x, y, dx, dy);
-
调用getDir(dx, dy)方法将激光移动方向转为整数,并用整型变量d保存
-
根据西北、东北、西南、东南四个不同的方向,返回1、2、4、8(具体哪个方向哪个数没关系)
-
-
判断visitedDirections[x][y] & d != 0:如果激光已经以当前方向访问过该单元格,则停止递归,避免死循环
-
判断gridState[x][y] == 0 && visitedDirections[x][y] == 0:如果是首次访问空心单元格,则增加total计数
-
visitedDirections[x][y] |= d:标记当前单元格已被访问
-
计算下一个单元格的位置nx,ny
-
判断gridState[nx][ny] == 1,处理激光遇到实心单元格时的反射和折射
-
判断gridState[nx][y] == gridState[x][ny],如果激光同时遇到两个实心单元格,则原路返回
-
判断gridState[nx][y] == 1:如果激光在x方向遇到实心单元格,则仅在x方向反射
-
判断gridState[x][ny]=1:如果激光在y方向遇到实心单元格,则仅在y方向反射
-
-
递归调用
-
-
输出total
Java代码
import java.util.Scanner;
public class Main {
static int[][] gridState = new int[1002][1002]; //网格中各个单元格的状态(空心/实心)
static int[][] visitedDirections = new int[1002][1002];//激光访问过的单元格以及方向
static int total = 0; //统计穿越空心单元格的总数
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(), m = scanner.nextInt();
int k = scanner.nextInt();
int x, y;
int dx, dy;
String dir;
for (int i = 0; i < m+2; i++) { //
gridState[0][i] = 1;
gridState[n+1][i] = 1;
}
for (int i = 0; i < n+2; i++) {
gridState[i][0] = 1;
gridState[i][m+1] = 1;
}
for (int i = 0; i < k; i++) {
x = scanner.nextInt();
y = scanner.nextInt();
gridState[x][y] = 1;
}
x = scanner.nextInt();
y = scanner.nextInt();
dir = scanner.next();
if(dir.equals("NW")){
dx = -1;
dy = -1;
}else if(dir.equals("SW")){
dx = 1;
dy = -1;
}else if(dir.equals("NE")){
dx = -1;
dy = 1;
}else {
dx = 1;
dy = 1;
}
laser(x, y, dx, dy);
System.out.println(total); //
}
private static void laser(int x, int y, int dx, int dy) {
int d = getDir(dx,dy); // 获取激光方向的数值
if((visitedDirections[x][y] & d) != 0) return; // 如果激光已经以当前方向访问过该单元格,则停止递归,避免死循环
if(gridState[x][y] == 0 && visitedDirections[x][y] == 0) total++; // 如果是首次访问空心单元格,则增加计数
visitedDirections[x][y] |= d; // 标记当前单元格已被访问
// 计算下一个单元格的位置
int nx = dx + x;
int ny = dy + y;
// 处理激光遇到实心单元格时的反射和折射逻辑
if(gridState[nx][ny] == 1){
if(gridState[nx][y] == gridState[x][ny]){ // 如果激光同时遇到两个实心单元格,则原路返回
nx = x;
ny = y;
dx = -dx;
dy = -dy;
} else if(gridState[nx][y] == 1){ // 如果激光在x方向遇到实心单元格,则仅在x方向反
nx = x;
dx = -dx;
}else{ // 如果激光在y方向遇到实心单元格,则仅在y方向反射
ny = y;
dy = -dy;
}
}
laser(nx, ny, dx, dy); // 递归调用,继续模拟激光传播
}
private static int getDir(int dx, int dy) { // 将激光移动的方向转换为整数,便于后续使用位运算处理
if(dx == -1 && dy == -1){
return 1;
}else if(dx == 1 && dy == -1){
return 2;
}else if(dx == -1 && dy == 1){
return 4;
}else {
return 8;
}
}
}