Algorithm/Programmers

[Lv.3] 공 이동 시뮬레이션 - 자바

시롱시롱 2023. 7. 13. 22:20
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

class Solution {
    public long solution(int n, int m, int x, int y, int[][] queries) {
        long answer = -1;
        Set<Point> pointList=new HashSet<>();
        pointList.add(new Point(x,y));
        for (int i = queries.length-1; i >=0; i--) {
            Set<Point> newPointSet=new HashSet<>();
            int type = queries[i][0];
            int dis = queries[i][1];
//            System.out.println("now type : "+type + " / dis : "+dis);
            for (Point point : pointList) {
                if(type==0){
                    //left
                    //현재 점을 왼쪽으로 dis만큼 와서 도착한 것이다.
                    //현재 점이 왼쪽에 붙어 있다면
                    //현재 점에서 오른쪽으로 0칸 ~ dis칸 만큼까지 점에서 도착할 수 있다.

                    //dis =3 >
                    if(point.y==0){
                        for (int j = 0; j <= Math.min(dis,m-1); j++) {
                            newPointSet.add(new Point(point.x, j));
                        }
                    }else{
                        //그렇지 않다면 정확히 dis만큼 떨어진 점에서만 올 수 있다. -> dis만큼 오른쪽 점이 overflow라면 pass
                        if(dis<=m-1- point.y)
                            newPointSet.add(new Point(point.x, point.y+dis));
                    }


                } else if (type == 1) {
                    //right
                    if(point.y==m-1){
                        for (int j = 0; j <= Math.min(dis,m-1); j++) {
                            newPointSet.add(new Point(point.x, m-1-j));
                        }
                    }else{
                        if(dis<=point.y)
                            newPointSet.add(new Point(point.x, point.y-dis));
                    }
                } else if (type == 2) {
                    //up
                    if(point.x==0){
                        for (int j = 0; j <= Math.min(dis,n-1); j++) {
                            newPointSet.add(new Point(j, point.y));
                        }
                    }else{
                        if(dis<=n-1- point.x)
                            newPointSet.add(new Point(point.x+dis, point.y));
                    }
                }else{
                    //down
                    if(point.x==n-1){
                        for (int j = 0; j <= Math.min(dis,n-1); j++) {
                            newPointSet.add(new Point(n-1-j, point.y));
                        }
                    }else{
                        if(dis<=point.x)
                            newPointSet.add(new Point(point.x-dis, point.y));
                    }
                }
            }
            pointList=newPointSet;
//            System.out.println("now Possible Size : "+newPointSet.size()+ " / have to same : "+pointList.size());
//            for (Point point : newPointSet) {
//                System.out.println("point = " + point);
//            }
        }
        return pointList.size();
    }
    class Point{
        @Override
        public String toString() {
            return "Point{" +
                    "x=" + x +
                    ", y=" + y +
                    '}';
        }

        int x;

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Point point = (Point) o;

            if (x != point.x) return false;
            return y == point.y;
        }

        @Override
        public int hashCode() {
            int result = x;
            result = 31 * result + y;
            return result;
        }

        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }

        int y;
    }
}

처음엔 위처럼, 도착지에서 가능한 출발지를 역으로 하나하나 구하는 방식으로 풀었었다.

대부분이 시간초과가 발생했고, 다른 풀이가 필요하다고 느꼈으나 역으로 생각하는건 무조건 맞다고 생각했었기에 다른 생각이 잘 안나 다른 분들의 풀이를 참고했다.

 

역으로 탐색한다는 것은 당연히 맞지만, 그 과정에서 모든 점들에 대해 검사를 하는 것이 아닌 가능한 후보군의 범위만 수정하면 되는 것이였다.

 

이분이 남겨주신 풀이를 보고 바로 이해할 수 있었다. (상당히 자세하고 이해하기 쉽게 설명해주셨음..)

 

이동 자체가 상,하,좌,우로만 이동하기에 어차피 후보군들은 모두 직사각형 구조로 나타낼 수 있게 된다.

 

그렇기에, 왼쪽 위점, 오른쪽 아래점만 변수로 저장하고 각 이동에 대해 역으로 반복하며 직사각형의 범위만 수정해주면 되는 문제인 것이다.

현재 도착점이 벽에 붙어 있는 경우 현재 직사각형 범위가 늘어난다는 것만 이해하면 쉽게 풀 수 있는 문제다.

 

정답코드

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

class Solution {
    public long solution(int n, int m, int x, int y, int[][] queries) {
        long answer = -1;
        long x1,x2,y1,y2;
        x1=x2=x;
        y1=y2=y;
        for (int i = queries.length-1; i >=0; i--) {
            int type = queries[i][0];
            long dis = queries[i][1];

            if(type==0){
                //left
                if(y1>0) {
                    y1 += dis;
                    if(y1>=m)
                        return 0;
                }
                y2=Math.min(y2+dis,m-1);
            } else if (type == 1) {
                //right
                if (y2 < m-1) {
                    y2 -= dis;
                    if (y2 < 0) {
                        return 0;
                    }
                }
                y1=Math.max(y1-dis,0);
            } else if (type == 2) {
                //up
                if (x1 > 0) {
                    x1 += dis;
                    if (x1 >= n) {
                        return 0;
                    }
                }
                x2=Math.min(x2+dis,n-1);
            }else{
                //down
                if (x2 < n-1) {
                    x2 -= dis;
                    if (x2 < 0) {
                        return 0;
                    }
                }
                x1=Math.max(x1-dis,0);
            }
        }
        return (x2-x1+1)*(y2-y1+1);
    }
}