Problem Solving (67)

문제 링크

https://www.codetree.ai/training-field/frequent-problems/problems/maze-runner/description?page=3&pageSize=20

 

풀이

배열 회전과 위치 갱신이 복잡한 문제였다.

구현 자체는 문제 조건에서 지정한 대로 따라가면 된다.

구현에서 신경을 썼던 부분은 아래와 같다.

  1. 모든 사람이 탈출하고 나서 회전하는 경우
  2. 배열을 회전하고 나서 사람의 위치를 갱신하는 경우

 

1번은 모두 탈출했는지 탐색하고 나서 이동과 회전을 진행했는데, 회전할 정사각형의 범위를 0으로 초기화 해줘야 했다.

 

2번은 인덱스 매칭으로 회전한 위치를 구해야 한다.

처음에는 맵을 회전할때에는

  1. 사람과 출구 위치를 맵에 넣음
  2. 회전 범위에 따라 회전
  3. 회전하면서 사람 위치와 출구라면 새로운 위치로 갱신해줌

이런 로직이었는데 다음과 같은 예시에서 문제가 생겼다.

사람1명 8 사람2명
0 1 2
3 9 출구

이때 위의 로직을 따르면 사람 1명을 먼저 갱신하고 사람 2명의 위치에서 모든 사람의 위치가 하나로 갱신되는 문제가 있었다.

 

따라서 다음과 같이 로직을 변경했다

  1. 사람 수만큼 반복문
  2. 현재 사람이 탈출하지 않았고, 회전 범위에 있는지 확인
  3. 회전 범위에 있다면 (0,0) 기준으로 위치를 이동
  4. [x][y] -> [y][박스크기 - x - 1] 공식 적용
  5. 기준점으로 위치 이동한 만큼 다시 더해줌

 

이 부분을 조심해서 풀었다.

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {
    static class Pos {
        int x, y;
        boolean isOut;

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

        public Pos(int x, int y, boolean isOut) {
            this.x = x;
            this.y = y;
            this.isOut = isOut;
        }
    }

    static int N, M, K, map[][], copy[][], total;
    static Pos exit; //출구 위치
    static int bx, by, boxSize; //회전할 박스 시작행열, 사이즈 저장
    static Pos[] p; //사람들 좌표 저장

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokens = new StringTokenizer(br.readLine());

        N = Integer.parseInt(tokens.nextToken()); //맵
        M = Integer.parseInt(tokens.nextToken()); //사람
        K = Integer.parseInt(tokens.nextToken()); //게임시간

        map = new int[N][N];
        p = new Pos[M];
        total = 0;

        for (int i = 0; i < N; i++) {
            tokens = new StringTokenizer(br.readLine());
            for (int j = 0; j < N; j++) {
                map[i][j] = Integer.parseInt(tokens.nextToken());
            }
        }

        for (int i = 0; i < M; i++) {
            tokens = new StringTokenizer(br.readLine());
            p[i] = new Pos(Integer.parseInt(tokens.nextToken()) - 1,
                    Integer.parseInt(tokens.nextToken()) - 1,
                    false);
        }

        tokens = new StringTokenizer(br.readLine());
        exit = new Pos(Integer.parseInt(tokens.nextToken()) - 1,
                Integer.parseInt(tokens.nextToken()) - 1);
        //end input

        while (K-- > 0) {
            //모든 참가자가 탈출했는지 확인
            if (isAllOut()) break;

            for (int i = 0; i < M; i++) {
                movePeople(i); //모든 참가자 이동
                //이동했는데 출구인지 확인
                if (p[i].x == exit.x && p[i].y == exit.y) {
                    p[i].isOut = true;
                }
            }

            bx = by = boxSize = 0; //모두 탈출하고 회전시킬 경우
            findRect(); //상자 사이즈 찾음

            rotateRect(); //미로 회전
        }

        exit.x++;
        exit.y++;
        System.out.println(total);
        System.out.println(exit.x + " " + exit.y);
    }

    private static boolean isAllOut() {
        for (int i = 0; i < M; i++) {
            if (!p[i].isOut) return false;
        }
        return true;
    }

    private static void rotateRect() {
        //맵 복사
        copy = new int[N][];
        for (int i = 0; i < N; i++) {
            copy[i] = Arrays.copyOfRange(map[i], 0, N);
        }

        //복사한 값을 원본에 돌려서 넣기
        for (int i = bx, x = by; i < bx + boxSize; i++, x++) {
            for (int j = by, y = bx + boxSize - 1; j < by + boxSize; j++, y--) {
                map[i][j] = copy[y][x];

                //내구도 감소
                if (isArea(map[i][j], 1, 10))
                    map[i][j]--;
            }
        }

        //출구 위치 변경
        if (isArea(exit.x, bx, bx + boxSize) && isArea(exit.y, by, by + boxSize)) {
            //(0,0) 기준으로 옮김
            int nx = exit.x - bx;
            int ny = exit.y - by;

            //회전시킴
            //20 -> 00
            //10 -> 01
            //00 -> 02
            //[x][y] -> [y][size-x-1]
            exit.x = ny + bx;
            exit.y = boxSize - nx - 1 + by;
        }

        //사람 위치 변경
        for (int i = 0; i < M; i++) {
            if (p[i].isOut) continue;

            if (isArea(p[i].x, bx, bx + boxSize) && isArea(p[i].y, by, by + boxSize)) {
                int nx = p[i].x - bx;
                int ny = p[i].y - by;

                p[i].x = ny + bx;
                p[i].y = boxSize - nx - 1 + by;
            }
        }
    }

    private static void findRect() {
        for (int size = 2; size <= N; size++) { //만들 수 있는 사각형
            for (int i = 0; i < N - size + 1; i++) {
                for (int j = 0; j < N - size + 1; j++) {
                    //출구가 있는지 확인
                    if (!(isArea(exit.x, i, i + size) && isArea(exit.y, j, j + size))) {
                        continue;
                    }

                    boolean flag = false;
                    //사람이 있는지 확인
                    for (int m = 0; m < M; m++) {
                        if (p[m].isOut) continue; //출구에 있는 참가자 제외

                        Pos now = p[m];
                        if (i <= now.x && now.x < i + size
                                && j <= now.y && now.y < j + size) {
                            flag = true;
                        }
                    }

                    if (flag) {
                        bx = i;
                        by = j;
                        boxSize = size;
                        return;
                    }

                }//end j

            }//end i

        }//end size
    }

    private static void movePeople(int idx) {
        Pos now = p[idx];

        //이미 출구인 경우
        if (now.isOut) return;

        //행이 다르다면 상하로 움직이기
        if (now.x != exit.x) {
            int nx = now.x;
            int ny = now.y;

            if (nx > exit.x) nx--; //위로 이동
            else nx++; //아래로 이동

            if (isRange(nx, ny) && map[nx][ny] == 0) {
                //움직일 수 있음
                now.x = nx;
                now.y = ny;
                p[idx] = now;
                total++;
                return;
            }
        }

        //열이 다르다면 좌우로 움직이기
        if (now.y != exit.y) {
            int nx = now.x;
            int ny = now.y;

            if (ny > exit.y) ny--; //좌로 이동
            else ny++; //우로 이동

            if (isRange(nx, ny) && map[nx][ny] == 0) {
                now.x = nx;
                now.y = ny;
                p[idx] = now;
                total++;
            }
        }
    }

    private static boolean isArea(int x, int min, int max) {
        if (min <= x && x < max) return true;
        return false;
    }

    private static boolean isRange(int x, int y) {
        if (x < 0 || y < 0 || x >= N || y >= N) return false;
        return true;
    }
}

문제 링크

https://www.acmicpc.net/problem/1036

 

풀이

이해하고 푸는데 한참 걸렸다. 진짜.. 진짜 오래 걸렸다 진짜...........

 

36진법으로 변환할 때 0-9, A-Z에서 해당하는 자리수를 Z로 변환했을 때 가장 큰 차이가 나는 것을 선택해야 한다.

말로 풀어서 설명하기 어려운데, 예를 들어서 예제의 HELLO는 다음과 같이 표현할 수 있다.

H 17 * 36^4
E 14 * 36^3
L 21 * 36^2 + 21 * 36^1
O 24 * 36^0

이것을 Z로 변환했을 때 차이가 가장 큰 값을 찾으려면 아래와 같이 변환하면 된다.

H (35-17) * 36^4
E (35-14) * 36^3
L (35-21) * 36^2 + (35-21) * 36^1
O (35-24) * 36^0

따라서 0-9, A-Z 값을 저장할 배열을 만들어주고, 해당 인덱스에 맞게 위의 표에 맞게 자리수를 넣어준다.

배열을 정렬해주고 난 후 가장 차이가 큰 수를 K개 더해준다.

 

간단한 예로 들면,

a = 1
b = 2
z = 10

a + b = 3 //총 합으로 사용

z - a = 9
z - b = 8

이때 a를 선택한다.

총합에 z-a의 차이를 더해주면 a + b -> 3 + (z - a) = 3 + 9 = 12가 된다.

위와 같은 예시로 N만큼 입력을 받고 36진수로 변환해서 일단 모든 숫자의 총합을 구해준다.

Z로 변환했을 때 가장 큰 차이를 가지는 상위 K개가 가지고 있는 차이값을 총합에 더해주면 구하고자 하는 최대값이 된다.

따라서 어떤 문자를 바꿀 것인지는 몰라도 된다.

 

자바의 long 타입은 50자를 받지 못하기 때문에 BigInteger를 사용한다.

질문게시판에서 반례를 참고했다

2
99999999999999999999999999999999999999999999999999
1
1

답
100000000000000000000000000000000000000000000000000

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int N = Integer.parseInt(br.readLine());

        //0) 초기화
        // 0-9, A-Z의 36진수 값을 저장할 배열 설정
        BigInteger[] digit = new BigInteger[36];
        Arrays.fill(digit, BigInteger.ZERO); //초기화

        //총합
        BigInteger sum = BigInteger.ZERO;

        for (int i = 0; i < N; i++) {
            String str = br.readLine();
            BigInteger cur = new BigInteger(str, 36); //36진수로 변환

            //1) 각 36진수를 총합에 더해줌
            sum = sum.add(cur);

            //2) 한글자씩 Z로 변환했을 때 가중치를 저장
            //ex) YZ라면, digit[Y] += (35 - Y) * 36^1을 저장함
            BigInteger pow = BigInteger.ONE; //지수 값
            for (int j = str.length() - 1; j >= 0; j--) {
                int idx = str.charAt(j) <= '9' ? str.charAt(j) - '0' : str.charAt(j) - 'A' + 10; //digit에 저장할 인덱스 설정
                digit[idx] = digit[idx].add(pow.multiply(BigInteger.valueOf(35 - idx)));
                pow = pow.multiply(BigInteger.valueOf(36)); //다음 자리수로
            }
        }

        //3) digit을 정렬하고 내림차순으로 사용해서 K만큼 더해줌
        //ex) 1+2 = 3을 10+2=12로 변경했을 때, 10-1의 차이인 9만큼 더해줘야 총 합이 됨
        int K = Integer.parseInt(br.readLine());
        Arrays.sort(digit);
        int cnt = 0;
        for (int i = 35; i >= 0 ; i--) {
            if(cnt == K) break;
            sum = sum.add(digit[i]);
            cnt++;
        }

        //4) 출력
        System.out.println(sum.toString(36).toUpperCase());

    }

}

처음에는 하나하나 전부 36 -> 10 -> 36 변환해가면서 구했는데 로직 자체가 틀렸었다.

이런 수 연산같은걸 생각해야 하는 문제가 아직도 어렵다...

문제 링크

https://www.acmicpc.net/problem/14499

 

풀이

방향에 따라 움직일 때 마다 주사위의 값을 변경해준다.

주사위가 회전할 때마다 아래와 같이 모양이 변한다

기본 동쪽 서쪽 북쪽 남쪽
   2
4 1 3
   5
   6
   2
6 4 1
   5
   3
   2
1 3 6
   5
   4
   1
4 5 3
   6
   2
   6
4 2 3
   1
   5

따라서 방향이 전환되면 그 방향에 따라 주사위의 위치에 따른 값을 전부 변경해준다.

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
    static int N, M, map[][], ju[];
    static int[][] dir = {{0, 1}, {0, -1}, {-1, 0}, {1, 0}}; //동서북남
    static StringBuilder sb;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokens = new StringTokenizer(br.readLine());

        N = Integer.parseInt(tokens.nextToken());
        M = Integer.parseInt(tokens.nextToken());
        map = new int[N][M];

        int x = Integer.parseInt(tokens.nextToken());
        int y = Integer.parseInt(tokens.nextToken());

        int K = Integer.parseInt(tokens.nextToken());

        for (int i = 0; i < N; i++) {
            tokens = new StringTokenizer(br.readLine());
            for (int j = 0; j < M; j++) {
                map[i][j] = Integer.parseInt(tokens.nextToken());
            }
        }

        //주사위 설정
        ju = new int[7];

        tokens = new StringTokenizer(br.readLine());
        sb = new StringBuilder();

        for (int i = 0; i < K; i++) {
            int[] now = roll(x, y, Integer.parseInt(tokens.nextToken()) - 1, sb);
            if(now == null) continue;
            x = now[0];
            y = now[1];
        }

        System.out.print(sb);

    }//end main

    private static int[] roll(int x, int y, int d, StringBuilder sb) {
        //방향대로 다음 위치 확인
        x += dir[d][0];
        y += dir[d][1];

        if (!isRange(x, y)) return null;

        changeJu(d);

        if(map[x][y] == 0){
            map[x][y] = ju[6];
        }else{
            ju[6] = map[x][y];
            map[x][y] = 0;
        }

        sb.append(ju[1]).append("\n"); //가장 윗면의 값 출력

        return new int[]{x, y};

    }

    private static boolean isRange(int x, int y) {
        if (x < 0 || y < 0 || x >= N || y >= M) return false;
        return true;
    }

    private static void changeJu(int d) {
        //굴리는 방향에 따라서 위치를 변경해줌. 1은 가장 윗면, 6은 가장 밑면이 됨
        //기본    1 2 3 4 5 6
        //오른    4 2 1 6 5 3
        //왼쪽    3 2 6 1 5 4
        //위쪽    5 1 3 4 6 2
        //남쪽    2 6 3 4 1 5

        int[] t = ju.clone();

        if (d == 0) { //동
            ju[1] = t[4];
            ju[3] = t[1];
            ju[4] = t[6];
            ju[6] = t[3];
        } else if (d == 1) { //서
            ju[1] = t[3];
            ju[3] = t[6];
            ju[4] = t[1];
            ju[6] = t[4];
        } else if (d == 2) { //북
            ju[1] = t[5];
            ju[2] = t[1];
            ju[5] = t[6];
            ju[6] = t[2];
        } else { //남
            ju[1] = t[2];
            ju[2] = t[6];
            ju[5] = t[1];
            ju[6] = t[5];
        }
    }

}

'Problem Solving > BOJ' 카테고리의 다른 글

[백준 Java] 1036 36진수  (0) 2023.07.20
[백준 Java] 3190 뱀  (0) 2023.06.27
[백준 Java] 14940 쉬운 최단거리  (0) 2023.06.23
[백준 Java] 9461 파도반 수열  (0) 2023.06.23
[백준 Java] 11724 연결 요소의 개수  (0) 2023.06.23

문제 링크

https://www.acmicpc.net/problem/3190

 

풀이

뱀의 위치를 Queue에 담아서 관리한다. 꼬리가 줄어들어야 할 때, 머리가 늘어날 때 큐의 앞뒤로 삽입 삭제 연산을 해야 하기 때문에 ArrayDeque를 사용했다.

방향은 오른쪽으로 가는 방향에서 시작하여, 오른쪽으로 90도씩 방형을 전환하도록 방향 배열에 설정해두었고, 각 방향 전환이 일어날 때 1씩 더하고 빼는 연산에 모듈러 연산을 적용했다.

while 문 안에서의 연산은 다음과 같다.

  1. 머리를 늘린다.
  2. 다음 위치가 벽인지, 자기 자신인지 확인한다.
  3. 사과가 있는지 확인한다.
  4. 사과가 없다면 꼬리 제거
  5. 새로운 머리를 넣어준다.
  6. 지금 초에 방향 전환이 있는지 확인한다.

이 순서로 진행했다.

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.StringTokenizer;

public class Main {
    static int N, map[][], time[];

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokens;

        N = Integer.parseInt(br.readLine()); //맵 크기
        map = new int[N][N];
        time = new int[10001];

        int K = Integer.parseInt(br.readLine()); //사과의 개수
        int x, y;
        for (int i = 0; i < K; i++) {
            tokens = new StringTokenizer(br.readLine());
            x = Integer.parseInt(tokens.nextToken()) - 1;
            y = Integer.parseInt(tokens.nextToken()) - 1;
            map[x][y] = 1;
        }

        K = Integer.parseInt(br.readLine()); //방향 정보
        char w;
        for (int i = 0; i < K; i++) {
            tokens = new StringTokenizer(br.readLine());
            x = Integer.parseInt(tokens.nextToken());
            w = tokens.nextToken().charAt(0);

            time[x] = w == 'D' ? 1 : -1; //왼쪽 -1, 오른쪽 1 저장
        }

        System.out.println(play());

    }

    private static int play() {
        int res = 0; //게임 진행 시간
        int[][] dir = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; //오른쪽으로 시계방향
        int d = 0; //오른쪽
        ArrayDeque<int[]> pos = new ArrayDeque<>(); //뱀의 모든 위치 저장
        pos.offer(new int[]{0, 0}); //머리
        //자기 자신 표시
        map[0][0] = 999;

        int nx, ny;
        while (true) {
            res++;

            int[] cur = pos.peek();

            //1. 머리 늘리기
            nx = cur[0] + dir[d][0];
            ny = cur[1] + dir[d][1];

            //2. 벽인지, 자기자신인지 확인
            if (!isRange(nx, ny) || map[nx][ny] == 999) break;

            //3. 사과 유무 확인

            if (map[nx][ny] == 0) { //사과를 안먹는 경우
                int[] tail = pos.pollLast();
                map[tail[0]][tail[1]] = 0;
            }
            map[nx][ny] = 999;
            pos.offerFirst(new int[]{nx, ny});
            
            //4. 초마다 방향 전환이 있는지 확인 후 방향을 바꿔줌
            if (time[res] == -1) {
                d = (d + 3) % 4; //빼기로 진행하면 마이너스값이 나오기 때문에 3을 더해서 모듈러 연산
            } else if (time[res] == 1) {
                d = (d + 1) % 4;
            }
        }

        return res;
    }

    private static boolean isRange(int x, int y) {
        if (x < 0 || y < 0 || x >= N || y >= N) return false;
        return true;
    }
}

문제 링크

https://www.acmicpc.net/problem/14940

 

풀이

bfs로 풀었다. 2인 지점에서 시작하고 이동할때마다 가중치를 증가시킨다.

배열은 입력받은 배열 map과 결과를 저장할 배열 res 두개를 사용한다. map에서 1로 갈 수 있는데, res에서 배열 초기값인 0이라면 도달하지 못했기 때문에 -1로 출력한다.

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {
    static int N, M, map[][], res[][];

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokens = new StringTokenizer(br.readLine());

        N = Integer.parseInt(tokens.nextToken());
        M = Integer.parseInt(tokens.nextToken());

        map = new int[N][M];
        res = new int[N][M];

        Queue<int[]> q = new ArrayDeque<>();
        boolean[][] v = new boolean[N][M];

        for (int i = 0; i < N; i++) {
            tokens = new StringTokenizer(br.readLine());
            for (int j = 0; j < M; j++) {
                map[i][j] = Integer.parseInt(tokens.nextToken());
                if (map[i][j] == 2) {
                    q.offer(new int[]{i, j, 0});
                    v[i][j] = true;
                    map[i][j] = 0;
                }
            }
        }

        bfs(q, v);

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                //map은 1인데 res에 0으로 되어있으면 도달하지 못함
                if (map[i][j] == 1 && res[i][j] == 0) {
                    sb.append(-1);
                } else {
                    sb.append(res[i][j]);
                }
                sb.append(" ");
            }
            sb.append("\n");
        }
        System.out.print(sb);

    }

    private static void bfs(Queue<int[]> q, boolean[][] v) {
        int[][] dir = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

        while (!q.isEmpty()) {
            int[] cur = q.poll();

            for (int d = 0; d < 4; d++) {
                int nx = cur[0] + dir[d][0];
                int ny = cur[1] + dir[d][1];

                if (!isRange(nx, ny) || v[nx][ny] || map[nx][ny] == 0) continue;

                res[nx][ny] = cur[2] + 1;
                v[nx][ny] = true;
                q.offer(new int[]{nx, ny, cur[2] + 1});
            }
        }
    }

    private static boolean isRange(int x, int y) {
        if (x < 0 || y < 0 || x >= N || y >= M) return false;
        return true;
    }
}

'Problem Solving > BOJ' 카테고리의 다른 글

[백준 Java] 14499 주사위 굴리기  (0) 2023.06.28
[백준 Java] 3190 뱀  (0) 2023.06.27
[백준 Java] 9461 파도반 수열  (0) 2023.06.23
[백준 Java] 11724 연결 요소의 개수  (0) 2023.06.23
[백준 Java] 11403 경로 찾기  (0) 2023.06.22

문제 링크

https://www.acmicpc.net/problem/9461

풀이

수열의 규칙을 찾는다.

1 1 1 2 2 3 4 5 6 7 9 12를 봤을 때, 1 1 1 2 2 까지는 규칙을 찾기 어렵지만, 3부터는 n-1과 n-5의 값이 합쳐진게 답이 된다.

문제에서 P(1)부터 P(10)까지 첫 10개 숫자는 1, 1, 1, 2, 2, 3, 4, 5, 7, 9이다. 라고 되어있기 때문에 각 단계별로 배열을 만든 다음 값을 사용한다.

N이 100까지 들어오기 때문에 배열의 크기를 100으로 잡고, 값이 int 자료형의 범위를 벗어나기 때문에 long을 사용한다.

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        //int 자료형을 사용하면 오버플로우 발생
        long[] dp = new long[101];
        dp[1] = 1;
        dp[2] = 1;
        dp[3] = 1;
        dp[4] = 2;
        dp[5] = 2;
        //이후 값은 dp[n-1] + dp[n-5] 값을 따름
        for (int i = 6; i < dp.length; i++) {
            dp[i] = dp[i-1] + dp[i-5];
        }

        int T = Integer.parseInt(br.readLine());
        StringBuilder sb = new StringBuilder();

        while(T-- > 0){
            int N = Integer.parseInt(br.readLine());
            sb.append(dp[N]).append("\n");
        }
        System.out.println(sb);
    }
}

문제 링크

https://www.acmicpc.net/problem/11724

풀이

유니온 파인드를 사용했다

  • find : 배열의 값(부모)이 들어온 숫자와 같다면 그 값을 돌려준다. 다르다면 부모의 값을 계속 타고 올라가며 찾는다.
  • union : find를 사용하여 각 부모를 찾는다. 두 값의 부모가 같다면 false를 돌려준다. 값이 다를 경우 작은 쪽을 부모로 만들어주고 true를 돌려준다

마지막에 N만큼 순회하며 부모의 값을 집합에 넣어주고, 집합의 크기를 출력한다.

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;

public class Main {
    static int N, parent[];

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokens = new StringTokenizer(br.readLine());

        N = Integer.parseInt(tokens.nextToken());
        int M = Integer.parseInt(tokens.nextToken());

        parent = new int[N];

        initArr();

        for (int i = 0; i < M; i++) {
            tokens = new StringTokenizer(br.readLine());
            union(Integer.parseInt(tokens.nextToken()) - 1, Integer.parseInt(tokens.nextToken()) - 1);
        }

        Set<Integer> set = new HashSet<>();
        for (int i = 0; i < N; i++) {
            set.add(find(i));
        }
        System.out.println(set.size());
    }

    private static void initArr() {
        for (int i = 0; i < N; i++) {
            parent[i] = i;
        }
    }

    private static int find(int a) {
        if (parent[a] == a) return a;
        return find(parent[a]);
    }

    private static boolean union(int a, int b) {
        a = find(a);
        b = find(b);

        if (a == b) return false;

        if (a <= b) parent[b] = a;
        else parent[a] = b;
        return true;
    }
}

'Problem Solving > BOJ' 카테고리의 다른 글

[백준 Java] 14940 쉬운 최단거리  (0) 2023.06.23
[백준 Java] 9461 파도반 수열  (0) 2023.06.23
[백준 Java] 11403 경로 찾기  (0) 2023.06.22
[백준 Java] 16928 뱀과 사다리 게임  (0) 2023.06.22
[백준 Java] 9019 DSLR  (0) 2023.06.15

문제 링크

https://www.acmicpc.net/problem/11403

 

풀이

2가지 방법으로 풀었다.

dfs

  • 한 행에 대해서 방문 배열 초기화
  • 지금 행과 열이 1이라면 연속되는 곳을 찾기 위해 dfs 호출
  • dfs 내에서 방문 처리, j에 대해서 연결된 다른 노드를 찾고 시작점과 이어주기 위해 dfs 호출

플로이드 워셜

  • i와 k가 연결되고, k와 j가 연결된 경우 i와 j를 연결시켜줌

처음에는 플로이드 워셜이 생각 나지 않아 bfs, dfs 여러 방법으로 풀어봤었는데, 가중치가 없더라도 모든 간선을 확인하고, 연속해서 확인해야 하는 경우 플로이드 워셜을 사용하니 훨씬 더 간단하게 풀렸다. 또한 input이 100까지 들어오기 때문에 100^3번 수행한다.

아래는 dfs, 위에는 플로이드 워셜로 풀었는데 둘 다 100^3을 수행해서 메모리와 시간상 별 차이는 없다.

코드 1 - dfs

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
    static int N, map[][], res[][];
    static boolean[] v;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokens;

        N = Integer.parseInt(br.readLine());
        map = new int[N][N];
        res = new int[N][N];

        for (int i = 0; i < N; i++) {
            tokens = new StringTokenizer(br.readLine());
            for (int j = 0; j < N; j++) {
                map[i][j] = Integer.parseInt(tokens.nextToken());
            }
        }//end input

        for (int i = 0; i < N; i++) {
            v = new boolean[N];

            for (int j = 0; j < N; j++) {
                if(map[i][j] == 1 && !v[j]){
                    dfs(i, j);
                }
            }
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                sb.append(res[i][j]).append(" ");
            }
            sb.append("\n");
        }
        System.out.println(sb);

    }

    private static void dfs(int x, int y) {
        //x, y가 1인 상태
        //y에 연결된 다른 간선 찾아야 함
        v[y] = true;
        res[x][y] = 1;

        for (int i = 0; i < N; i++) {
            if(map[y][i] == 1 && !v[i]){
                //y와 연결된 간선을 시작한 노드와 이어
                dfs(x, i);
            }
        }
    }

}

코드 2 - 플로이드 워셜

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokens;

        int N = Integer.parseInt(br.readLine());
        int[][] map = new int[N][N];

        for (int i = 0; i < N; i++) {
            tokens = new StringTokenizer(br.readLine());
            for (int j = 0; j < N; j++) {
                map[i][j] = Integer.parseInt(tokens.nextToken());
            }
        }//end input

        for (int k = 0; k < N; k++) {
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    if (map[i][k] == 1 && map[k][j] == 1) {
                        map[i][j] = 1;
                    }
                }
            }
        }


        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                sb.append(map[i][j]).append(" ");
            }
            sb.append("\n");
        }
        System.out.print(sb);

    }

}

문제 링크

https://www.acmicpc.net/problem/16928

풀이

  • bfs로 주사위 6개에 대해서 탐색
  • 시작 위치는 1
  • 배열에 맵의 정보 설정
  • 100이 넘지 않는 경우 맵을 이동하는지 확인 후 갱신해주기

기본 bfs에서 이 조건을 지켜서 풀었다. 처음에는 시작 위치를 0으로 잡았는데, 문제 조건에서 1에서 시작한다고 되어있기 때문에 1로 해야 한다.

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokens = new StringTokenizer(br.readLine());

        int N = Integer.parseInt(tokens.nextToken());
        int M = Integer.parseInt(tokens.nextToken());

        int[] map = new int[101];

        int a, b;
        for (int i = 0; i < N + M; i++) {
            tokens = new StringTokenizer(br.readLine());
            a = Integer.parseInt(tokens.nextToken());
            b = Integer.parseInt(tokens.nextToken());

            map[a] = b;
        }

        System.out.println(bfs(map));
    }

    private static int bfs(int[] map) {
        int res = 0;
        Queue<int[]> q = new ArrayDeque<>();
        boolean[] v = new boolean[101];

        q.offer(new int[]{1, 0}); //위치와 횟수
        v[1] = true;

        while (!q.isEmpty()) {
            int[] cur = q.poll();

            //1~6 주사위를 굴림
            for (int d = 1; d <= 6; d++) {
                int next = cur[0] + d;

                if (v[next] || next > 100) continue;

                //도착
                if (next == 100) {
                    res = cur[1] + 1;
                    return res;
                }

                //맵 방향 설정
                if (map[next] != 0) {
                    next = map[next];
                }
                //다음 위치
                v[next] = true;
                q.offer(new int[]{next, cur[1] + 1});

            }
        }

        return res;
    }
}

문제 링크

https://www.acmicpc.net/problem/9019

풀이

bfs 문제인데 L, R 연산 계산하는 법에서 시간이 많이 걸렸다.

Queue에 저장할 class를 선언해주고, 이 안에 D,S,L,R 연산을 추가했다.

이러면 각 연산에서 값이 달라졌어도 cur 값이 유지가 가능하다.

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokens;
        StringBuilder sb = new StringBuilder();

        int T = Integer.parseInt(br.readLine());

        while (T-- > 0) {
            tokens = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(tokens.nextToken());
            int b = Integer.parseInt(tokens.nextToken());

            sb.append(bfs(a, b)).append("\n");
        }
        System.out.println(sb);
    }

    static class Pos {
        int num;
        String order;

        public Pos(int num, String order) {
            this.num = num;
            this.order = order;
        }

        public int D() {
            return (num * 2) % 10000;
        }

        public int S() {
            return num == 0 ? 9999 : num - 1;
        }

        public int L() {
            //1234 -> 2341
            //123 -> 231
            //12 -> 21
            return (num % 1000) * 10 + (num / 1000);
        }

        public int R() {
            //1234 -> 4123
            return (num % 10) * 1000 + (num / 10);
        }

    }

    private static String bfs(int a, int b) {
        //D, S, L, R의 연산을 수행함
        Queue<Pos> q = new ArrayDeque<>();
        q.offer(new Pos(a, ""));
        boolean[] v = new boolean[10000];
        v[a] = true;

        String res = "";

        while (!q.isEmpty()) {
            Pos cur = q.poll();

            if (cur.num == b) {
                res = cur.order;
                break;
            }

            for (int i = 0; i < 4; i++) {
                Pos next;
                if (i == 0) {
                    next = new Pos(cur.D(), cur.order+"D");
                } else if (i == 2) {
                    next = new Pos(cur.S(), cur.order+"S");
                } else if (i == 3) {
                    next = new Pos(cur.L(), cur.order+"L");
                } else {
                    next = new Pos(cur.R(), cur.order+"R");
                }

                if (v[next.num]) continue;
                v[next.num] = true;
                q.offer(next);
            }

        }
        return res;
    }

}
1 2 3 4 ··· 7