激光镜像(模拟题)

【问题描述】

有一个 n×m 的网格,其中包含一些实心单元和一些空心单元。网格左上角的坐标为(1, 1),而右下角的坐标为(n, m)。其中有 k 个实心单元,而其他的则是空心的。这时从坐标为( x(s)y(s)( ))的单元中心向四个对角方向之一(也就是东北、西北、东南和西南)的方向发射一个激光束,如果激光束遇到实心单元或网格边缘则形成反射或折射,方式如下:

一段时间后,激光束将进入一个死循环,计算在进入死循环之前激光束穿越至少一次的空单元格总数,穿越是指穿过单元中心。

【输入形式】

输入的第一行包含三个整数 nm k (1≤nm≤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

解题思路

  1. 全局初始化:gridState[1002][1002]网格空心、实心状态、visitedDirctions [1002][1002]激光访问过的单元格和方向、total死循环前穿越过的空心单元格总数

  2. 命令行读取:n、m、k

  3. main函数变量初始化:x、y激光当前所在的单元格坐标,dx、dy激光移动的方向,dx激光在行上移动的增量,dy在列反向移动的增量、激光方向dir

  4. 初始化网络边界为实心,行:[0][i]、[n+1][i]、[i][0]、[i][m+1]都设置为1

  5. 设置实心单元格位置gridState[x][y]=1

  6. 命令行读取激光初始位置x、y、激光初始方向dir

  7. 分为四种情况,读入的激光初始方向dir、设置dx、dy

  8. 开始模拟激光,调用laser方法laser(x, y, dx, dy);

    1. 调用getDir(dx, dy)方法将激光移动方向转为整数,并用整型变量d保存

      1. 根据西北、东北、西南、东南四个不同的方向,返回1、2、4、8(具体哪个方向哪个数没关系)

    2. 判断visitedDirections[x][y] & d != 0:如果激光已经以当前方向访问过该单元格,则停止递归,避免死循环

    3. 判断gridState[x][y] == 0 && visitedDirections[x][y] == 0:如果是首次访问空心单元格,则增加total计数

    4. visitedDirections[x][y] |= d:标记当前单元格已被访问

    5. 计算下一个单元格的位置nx,ny

    6. 判断gridState[nx][ny] == 1,处理激光遇到实心单元格时的反射和折射

      1. 判断gridState[nx][y] == gridState[x][ny],如果激光同时遇到两个实心单元格,则原路返回

      2. 判断gridState[nx][y] == 1:如果激光在x方向遇到实心单元格,则仅在x方向反射

      3. 判断gridState[x][ny]=1:如果激光在y方向遇到实心单元格,则仅在y方向反射

    7. 递归调用

  9. 输出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;
        }
    }
}
  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值