10830번: 행렬 제곱

 

N*N 크기의 행렬 A, A의 B제곱을 구하라. 수가 매우 커질 수 있으니 A^B의 각 원소를 1,000으로 나눈 나머지를 출력.

2≤N≤5, 1≤B≤100,000,000,000

B가 아주 크다. 단순한 방법으로는 안된다.

행렬 A의 B제곱은 A의 B-1 제곱에 A를 곱한 것이다. 팩토리얼과 유사하다. 그런데 B가 최대이면 단순하게 순회를 1번만 돌아도 시간초과이다.

B가 100만이라고 해보자 A^100만 = (A^50만)^2

$$ A^B = {A^{(B/2)}}^2 $$

이러면 B가 1천억이라 하더라도 500억 → 250억 → 125억 이렇게 logN으로 감소시킬 수 있다.

그런데 B가 홀수냐 짝수냐에 따라서 처리를 좀 달리해야 한다. B가 짝수라면 그냥 해도 되지만 B가 홀수라면 1일 빼서 짝수로 만든 다음 해당 과정을 처리하고 마지막에 하나를 다시 곱해준다.

필요한 연산들은 다음과 같다.

  1. 행렬의 곱셈
  2. 거듭제곱의 재귀
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

    static int N;
    static int[][] matrix;
    static long B;
    static final int MOD = 1_000;

    static FastReader scan = new FastReader();

    static void input() {
        N = scan.nextInt();
        B = scan.nextLong();
        matrix = new int[N][N];

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                matrix[i][j] = scan.nextInt() % MOD;
            }
        }
    }

    static int[][] multiply(int[][] A, int[][] B) {
        int[][] C = new int[N][N];

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                for (int k = 0; k < N; k++) {
                    C[i][j] = (C[i][j] + (A[i][k] * B[k][j]) % MOD) % MOD;
                }
            }
        }
        return C;
    }

    static int[][] power(int[][] A, long B) {
        if (B == 1) {
            return A;
        }

        int[][] tmp = power(A, B / 2);
        tmp = multiply(tmp, tmp);

        if (B % 2 == 1) {
            tmp = multiply(tmp, matrix);
        }

        return tmp;
    }

    static void solve() {
        int[][] result = power(matrix, B);

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                System.out.print(result[i][j] + " ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        input();
        solve();
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 2933 - 미네랄  (0) 2024.11.05
BOJ 15591 - MooTube (Silver)  (0) 2024.10.31
BOJ 12865 - 평범한 배낭  (0) 2024.10.31
BOJ 15989 - 1,2,3 더하기 4  (0) 2024.10.26
BOJ 1528 - 금민수의 합  (0) 2024.10.26

12865번: 평범한 배낭

 

N개의 물건이 있다. 각 물건은 무게 W와 가치 V를 가진다.

최대 K만큼의 무게만 넣을 수 있는 배낭이 있을 때 가지고 갈 수 있는 물건들의 가치의 최댓값을 찾아라

1≤N≤100, 1≤K≤100,000, 1≤W≤100,000, 0≤V≤1,000

 

단위 무게당 가장 가치있는 물건을 넣어볼까 생각할 수 있다. 하지만 그러면 안 된다. 물건을 쪼갤 수 없을뿐더러 해당 경우가 최선의 경우인지 보장이 안된다.

완전탐색의 경우에는 해당 물건을 넣냐 안넣냐의 경우가 존재해 O(2^100)으로 불가능하다

결국 부분 문제의 해를 이용하는 DP를 써야한다.

dp 배열의 의미부터 정해보자. 물건을 넣냐 안 넣냐 와 무게 이 두 가지가 필요하다.

즉 dp[i][w] = i번 째 물건까지 고려했을 때, 무게 w를 사용해서 얻을 수 있는 최대 가치가 된다.

i 번 째 물건을 넣을 수 있는 경우가 넣을 수 없는 경우가 존재한다.

i번 째 물건을 넣을 수 있는데 넣는 경우와 안 넣는 경우도 존재한다.

i 번 째 물건을 넣을 수 있는 경우는 다음과 같다.

dp[i][w] = Math.max(dp[i-1][w], dp[i-1][w - weight[i]] + value[i])가 된다.

i 번 째 물건을 넣을 수 없는 경우는 다음과 같다.

dp[i][w] = dp[i-1][w]

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

public class Main {

    static class Item {
        int weight;
        int value;

        Item(int weight, int value) {
            this.weight = weight;
            this.value = value;
        }
    }

    static FastReader scan = new FastReader();
    static int N;
    static int K;
    static Item[] items;

    static void input() {
        N = scan.nextInt();
        K = scan.nextInt();
        items = new Item[N + 1];
        for (int i = 1; i <= N; i++) {
            Item item = new Item(scan.nextInt(), scan.nextInt());
            items[i] = item;
        }
    }

    static void solve() {
        int[][] dp = new int[N + 1][K + 1];
        for (int i = 1; i <= N; i++) {
            for (int w = 1; w <= K; w++) {
                if (items[i].weight <= w) {
                    // 해당 아이템을 넣을 수 있는 경우
                    dp[i][w] = Math.max(dp[i - 1][w], dp[i - 1][w - items[i].weight] + items[i].value);
                } else {
                    dp[i][w] = dp[i - 1][w];
                }
            }
        }
        System.out.println(dp[N][K]);
    }

    public static void main(String[] args) {
        input();
        solve();
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 15591 - MooTube (Silver)  (0) 2024.10.31
BOJ 10830 - 행렬의 제곱  (0) 2024.10.31
BOJ 15989 - 1,2,3 더하기 4  (0) 2024.10.26
BOJ 1528 - 금민수의 합  (0) 2024.10.26
BOJ 1655 - 가운데를 말해요  (0) 2024.10.25

15989번: 1, 2, 3 더하기 4

 

정수 n을 1, 2, 3의 합으로 나타내는 방법을 구해라.

n ≤ 10,000

재귀를 통해 모든 것을 확인하기에는 너무 많다.

숫자가 1, 2, 3이니 합을 표현할 때 1, 2, 3으로 끝나는 경우를 나눠보자.

2차원 배열로 dp를 하자.

int [][] dp; → dp [i][j]는 i를 만들 때 마지막 수가 j인 경우를 이야기한다.

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

public class Main {

    static FastReader scan = new FastReader();

    public static void main(String[] args) {
        int T = scan.nextInt();
        int[][] dp = new int[10002][4];
        dp[1][1] = 1;
        dp[2][1] = 1;
        dp[2][2] = 1;
        dp[3][1] = 1;
        dp[3][2] = 1;
        dp[3][3] = 1;

        for (int i = 4; i <= 10000; i++) {
            dp[i][1] = 1;
            dp[i][2] = dp[i-2][1] + dp[i-2][2];
            dp[i][3] = dp[i-3][1] + dp[i-3][2] + dp[i-3][3];
        }

        for (int p = 0; p < T; p++) {
            int N = scan.nextInt();
            System.out.println(dp[N][1] + dp[N][2] + dp[N][3]);
        }
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 10830 - 행렬의 제곱  (0) 2024.10.31
BOJ 12865 - 평범한 배낭  (0) 2024.10.31
BOJ 1528 - 금민수의 합  (0) 2024.10.26
BOJ 1655 - 가운데를 말해요  (0) 2024.10.25
BOJ 1944 - 복제 로봇  (2) 2024.10.23

1528번: 금민수의 합

 

금민수 = 4와 7로만 이루어진 수

숫자 N(N ≤ 1,000,000)을 금민수의 합으로 나타내라. 여러 방법이 존재한다면 수의 개수가 적은 것을 출력한다.

그러한 방법도 여러 개일 경우 사전 순으로 가장 앞서는 것을 출력.

표현할 수 없다면 -1

  1. 금민수의 합으로 나타내는 방법을 알아야 한다.
  2. 여러 방법이 존재하는지 확인하기 위해 저장을 해야 한다.
  3. 사전순으로 정렬은 문자열의 기본 정렬이다.

만약 N 보다 작은 금민수를 전부 찾아서 저장할 수 있을까? → 메모리상으로는 가능하다.

그런데 전부 찾아서 저장하더라도 해당 조합을 재귀로 찾기에는 너무 많다.

예를 들어 100을 생각해 보자. 100보다 작은 금민수는 4, 7, 44, 47, 74, 77이 존재한다.

100을 만들 수 있는가? = 23 or 26 or 56 or 93 or 96을 금민수의 조합으로 만들 수 있느냐

23 → 4, 7 → 19, 16을 만들 수 있는가

16 → 4, 7 → 12, 9를 만들 수 있는가

12 → 4, 7 → 8, 5를 만들 수 있는가

8 → 4, 7 → 4, 1을 만들 수 있는가

4 → 4 → 성공

DP다. 만들 수 없음을 -1을 두고 만들 수 있는 것은 최소 개수를 저장한다.

  1. 먼저 N보다 작은 금민수를 만든다.
  2. bottom - up으로 0부터 dp를 채운다.
  3. 최소개수가 더 적다면 갱신이 된다면 구성되는 숫자를 덮어 씌운다.
  4. 최소개수가 동일하다면 구성 숫자를 사전순으로 비교한 후 덮어 씌운다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;

public class Main {

    static FastReader scan = new FastReader();
    static int N;
    static final String[] nums = {"4", "7"};
    static List<Integer> fourSeven = new ArrayList<>();
    static final int INF = Integer.MAX_VALUE;

    static void input() {
        N = scan.nextInt();
    }

    static void getFourSeven(StringBuilder curr) {
        if (curr.length() > 0 && Integer.parseInt(curr.toString()) > N) {
            return;
        }

        if (curr.length() > 0 ) {
            fourSeven.add(Integer.parseInt(curr.toString()));
        }

        for (String num : nums) {
            curr.append(num);
            getFourSeven(curr);
            curr.deleteCharAt(curr.length() - 1);
        }
    }

    public static void main(String[] args) throws IOException {
        input();
        getFourSeven(new StringBuilder());
        Collections.sort(fourSeven);

        int[] dp = new int[N + 1];
        Arrays.fill(dp, INF);
        List<Integer>[] sequences = new ArrayList[N + 1];
        dp[0] = 0;
        sequences[0] = new ArrayList<>();

        for (int i = 0; i <= N; i++) {
            if (dp[i] == INF) continue;

            for (int num : fourSeven) {
                int next = i + num;
                if (next > N) continue;

                if (dp[i] + 1 < dp[next]) {
                    dp[next] = dp[i] + 1;
                    sequences[next] = new ArrayList<>(sequences[i]);
                    sequences[next].add(num);
                } else if (dp[i] + 1 == dp[next]) {
                    List<Integer> newSeq = new ArrayList<>(sequences[i]);
                    newSeq.add(num);

                    if (isFirst(newSeq, sequences[next])) {
                        sequences[next] = newSeq;
                    }
                }
            }
        }

        if (dp[N] == INF) {
            System.out.println(-1);
        } else {
            StringBuilder sb = new StringBuilder();
            for (int num : sequences[N]) {
                sb.append(num).append(" ");
            }
            System.out.println(sb.toString().trim());
        }
    }

    static boolean isFirst(List<Integer> newSeq, List<Integer> seq) {
        for (int i = 0; i < Math.min(newSeq.size(), seq.size()); i++) {
            if (!newSeq.get(i).equals(seq.get(i))) {
                return newSeq.get(i) < seq.get(i);
            }
        }
        return newSeq.size() < seq.size();
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

메모리 초과가 발생헀다.

 

dp 배열 크기는 4MB로 문제없지만 sequences의 실제 데이터에서 문제가 발생하는 것 같다.

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

public class Main {
    static FastReader scan = new FastReader();
    static int N;
    static final String[] nums = {"4", "7"};
    static List<Integer> fourSeven = new ArrayList<>();
    static final int INF = Integer.MAX_VALUE;

    static void input() {
        N = scan.nextInt();
    }

    static void getFourSeven(StringBuilder curr) {
        if (curr.length() > 0 && Integer.parseInt(curr.toString()) > N) {
            return;
        }

        if (curr.length() > 0) {
            fourSeven.add(Integer.parseInt(curr.toString()));
        }

        for (String num : nums) {
            curr.append(num);
            getFourSeven(curr);
            curr.deleteCharAt(curr.length() - 1);
        }
    }

    public static void main(String[] args) throws IOException {
        input();
        getFourSeven(new StringBuilder());
        Collections.sort(fourSeven);

        int[] dp = new int[N + 1];
        int[] prev = new int[N + 1];
        int[] used = new int[N + 1];
        Arrays.fill(dp, INF);
        Arrays.fill(prev, -1);
        dp[0] = 0;

        for (int i = 0; i <= N; i++) {
            if (dp[i] == INF) continue;

            for (int num : fourSeven) {
                int next = i + num;
                if (next > N) continue;

                if (dp[i] + 1 < dp[next]) {
                    dp[next] = dp[i] + 1;
                    prev[next] = i;
                    used[next] = num;
                } else if (dp[i] + 1 == dp[next]) {
                    List<Integer> currentPath = getPath(i, prev, used);
                    currentPath.add(num);
                    List<Integer> existingPath = getPath(next, prev, used);

                    if (isFirst(currentPath, existingPath)) {
                        prev[next] = i;
                        used[next] = num;
                    }
                }
            }
        }

        if (dp[N] == INF) {
            System.out.println(-1);
        } else {
            List<Integer> answer = getPath(N, prev, used);
            StringBuilder sb = new StringBuilder();
            for (int num : answer) {
                sb.append(num).append(" ");
            }
            System.out.println(sb.toString().trim());
        }
    }

    // 경로 복원 함수
    static List<Integer> getPath(int n, int[] prev, int[] used) {
        List<Integer> path = new ArrayList<>();
        while (n > 0) {
            path.add(used[n]);
            n = prev[n];
        }
        Collections.sort(path);
        return path;
    }

    static boolean isFirst(List<Integer> newSeq, List<Integer> seq) {
        for (int i = 0; i < Math.min(newSeq.size(), seq.size()); i++) {
            if (!newSeq.get(i).equals(seq.get(i))) {
                return newSeq.get(i) < seq.get(i);
            }
        }
        return newSeq.size() < seq.size();
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

위치를 저장하는 배열을 사용하도록 변경했다. 하지만 역시 메모리 초과가 발생했다…

뭐가 원인인지 파악하지 못해 풀이를 dp에서 다른 방식으로 찾아보기로 하였다.

 

BFS방식을 사용하도록 하겠다.

BFS는 레벨별 탐색 즉 사용한 금민수의 개수별로 탐색이 진행되기 때문에 원하는 답에 최소 개수로 도달할 수 있다. 또한 사용하는 금민수를 사전순으로 정리해 두면 자연스럽게 사전순으로 빠른 것을 먼저 찾는다.

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

public class Main {
    static FastReader scan = new FastReader();
    static int N;
    static final String[] nums = {"4", "7"};
    static List<Integer> fourSeven = new ArrayList<>();
    static final int INF = Integer.MAX_VALUE;

    static void input() {
        N = scan.nextInt();
    }

    static void getFourSeven(StringBuilder curr) {
        if (curr.length() > 0 && Integer.parseInt(curr.toString()) > N) {
            return;
        }

        if (curr.length() > 0) {
            fourSeven.add(Integer.parseInt(curr.toString()));
        }

        for (String num : nums) {
            curr.append(num);
            getFourSeven(curr);
            curr.deleteCharAt(curr.length() - 1);
        }
    }

    static class State {
        int sum;
        List<Integer> nums;

        State(int sum, List<Integer> nums) {
            this.sum = sum;
            this.nums = nums;
        }
    }

    public static void main(String[] args) throws IOException {
        input();
        getFourSeven(new StringBuilder());
        Collections.sort(fourSeven);

        Queue<State> queue = new LinkedList<>();
        boolean[] visited = new boolean[N + 1];

        queue.add(new State(0, new ArrayList<>()));
        visited[0] = true;

        while (!queue.isEmpty()) {
            State curr = queue.poll();

            if (curr.sum == N) {
                StringBuilder sb = new StringBuilder();
                for (int num : curr.nums) {
                    sb.append(num).append(" ");
                }
                System.out.println(sb.toString().trim());
                return;
            }

            for (int num : fourSeven) {
                int nextSum = curr.sum + num;

                if (nextSum > N || visited[nextSum]) continue;

                List<Integer> nextNums = new ArrayList<>(curr.nums);
                nextNums.add(num);

                queue.add(new State(nextSum, nextNums));
                visited[nextSum] = true;
            }
        }

        System.out.println(-1);
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 12865 - 평범한 배낭  (0) 2024.10.31
BOJ 15989 - 1,2,3 더하기 4  (0) 2024.10.26
BOJ 1655 - 가운데를 말해요  (0) 2024.10.25
BOJ 1944 - 복제 로봇  (2) 2024.10.23
BOJ 15684 - 사다리 조작  (0) 2024.10.11

1655번: 가운데를 말해요

 

정수가 하나씩 주어졌을 때 지금까지 언급된 수 중에서 중간값을 말해야 한다.

1 → 1

5 → 1, 5 → 1

2 → 1, 5, 2 → 1, 2, 5 → 2

10 → 1, 2, 5, 10 → 2

-99 → 1, 2, 5, 10, -99 → -99, 1, 2, 5, 10 → 2

7 → -99, 1, 2, 5, 10, 7 → -99, 1, 2, 5, 7, 10 → 2

5 → -99, 1, 2, 5, 7, 10, 5 → -99, 1, 2, 5, 5, 7, 10 → 5

 

리스트에 숫자를 추가해 가며 정렬하고 중간값을 출력하면 된다.

그렇지만 숫자가 추가될 때마다 정렬하고 중간값을 찾으면 시간초과가 발생할 것이다.

중간이라는 의미는 중간 숫자보다 작은 것과 큰 것으로 나눌 수 있다.

  1. 첫 입력이 mid값이다.
  2. mid를 기준으로 두 그룹으로 나누기 위해 왼쪽 그룹 최대힙과 오른쪽 그룹 최소힙을 설정한다.
  3. 다음 입력 값이 mid보다 작으면 왼쪽 그룹, 크다면 오른쪽 그룹으로 간다.
  4. 두 그룹간의 크기 비교를 통해 중간값 변경 작업을 수행한다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class Main {

    static FastReader scan = new FastReader();
    static int N;

    public static void main(String[] args) {
        N = scan.nextInt();
        List<Integer> result = new ArrayList<>();
        int mid = scan.nextInt();
        result.add(mid);
        PriorityQueue<Integer> left = new PriorityQueue<>((a,b) -> Integer.compare(b, a)); // 최대
        PriorityQueue<Integer> right = new PriorityQueue<>();

        for (int i = 1; i < N; i++) {
            int num = scan.nextInt();
            if (mid < num) {
                right.add(num);
            } else {
                left.add(num);
            }

            if (right.size() - left.size() > 1) {
                left.add(mid);
                mid = right.poll();
            } else if (left.size() - right.size() >= 1) {
                right.add(mid);
                mid = left.poll();
            }
            result.add(mid);
        }

        for (int num : result) {
            System.out.println(num);
        }
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 15989 - 1,2,3 더하기 4  (0) 2024.10.26
BOJ 1528 - 금민수의 합  (0) 2024.10.26
BOJ 1944 - 복제 로봇  (2) 2024.10.23
BOJ 15684 - 사다리 조작  (0) 2024.10.11
BOJ 14889 - 스타트와 링크  (1) 2024.10.11

1944번: 복제 로봇 (acmicpc.net)

 

모든 열쇠를 찾으면서 로봇이 움직이는 횟수의 합의 최소.

하나의 칸에 동시에 여러 로봇이 존재할 수 있다.

S와 K의 위치에서 로봇이 복제가 가능하다.

로봇은 최초에 1대 존재하며 S와 K에서 원하는 숫자만큼 복제할 수 있다.

→ 복제란 무엇이지? 최단 거리니 BFS를 생각해 보면 퍼저나 가는 모습이 마치 복제된 로봇들이 이동하는 것과 유사하다. 결국 복제는 하나의 로봇이 여러 경로로 갈 수 있다고 이야기하는 것 같다.

→ 특히 복제 한다는 부분에서 중요한 노드임을 판단할 수 있다.

결국 최단거리로 모든 열쇠를 수거해야 한다. 최단거리의 기반 알고리즘은 BFS로 하면 될 것 같다.

같은 지점을 반복해서 지나갈 수 있다.

→ 하였으므로 단순 배열로 방문 처리를 하면 안 된다. 경로 자체를 반복 가능한 간선으로 보고 각 노드를 방문 기준으로 처리해야 할 것 같다.

결국 노드와 간선으로 구성된 그래프 문제가 된다.

모든 노드를 최소 비용으로 연결하면 되는 문제가 되며 MST이다.

  1. 입력을 순회하며 S, K 위치를 저장한다. = 노드가 된다.
  2. 노드들 사이의 거리를 계산한다.
    1. 이게 곧 간선이다. 간선이니 시작, 도착, 거리 이렇게 세 필드가 필요하다.
    2. Edge라는 클래스에 저장하며 이 Edge들을 저장한다.
    3. 거리 계산은 한 노드에서 다른 노드들 사이 거리를 계산하는 것이다. bfs를 통해 계산한다.
  3. 저장된 Edge들을 가지고 최소신장트리를 구성한다.
    1. 가장 비용(거리)이 저렴한 것부터 꺼내서 두 노드를 연결한다.
    2. 노드를 연결하는데 이미 같은 집합에 있다면 패스
      1. UNION-FIND를 통해 해결
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {

    static FastReader scan = new FastReader();
    static int N, M;
    static int[][] DIRECTION = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    static char[][] map;
    static Map<String, Point> points = new HashMap<>(); // 시작점, 열쇠들이 저장될 공간
    static int[][] distances;

    static class Point {
        int id, r, c;

        Point(int id, int r, int c) {
            this.id = id;
            this.r = r;
            this.c = c;
        }

        String getKey() {
            return r + "," + c;
        }

        @Override
        public boolean equals(Object o) {

            if (!(o instanceof Point)) {
                return false;
            }
            Point p = (Point) o;

            return this.r == p.r && this.c == p.c;
        }
    }

    static class Edge implements Comparable<Edge> {
        int start, end, weight;

        Edge(int start, int end, int weight) {
            this.start = start;
            this.end = end;
            this.weight = weight;
        }

        @Override
        public int compareTo(Edge o) {
            return this.weight - o.weight;
        }
    }

    static void input() {
        N = scan.nextInt();
        M = scan.nextInt();
        map = new char[N][N];
        int id = 0;
        for (int i = 0; i < N; i++) {
            map[i] = scan.next().toCharArray();
            for (int j = 0; j < N; j++) {
                if (map[i][j] == 'S' || map[i][j] == 'K') {
                    Point point = new Point(id++, i, j);
                    points.put(point.getKey(), point);
                }
            }
        }

        int size = points.size();
        distances = new int[size][size];
        for (int[] row : distances) {
            Arrays.fill(row, Integer.MAX_VALUE);
        }
    }

    static void calculateDistances() {
        for (Point point : points.values()) {
            dfs(point);
        }
    }

    static void dfs(Point start) {
        Queue<Point> q = new LinkedList<>();
        int[][] visited = new int[N][N];
        q.add(start);
        visited[start.r][start.c] = 0;

        while (!q.isEmpty()) {
            Point curr = q.poll();
            // 시작점이 아니면서 다른 노드라면 거리 갱신
            if (!start.equals(curr) && points.containsKey(curr.getKey())) {
                int from = start.id;
                int to = points.get(curr.getKey()).id;
                int dist = visited[curr.r][curr.c];
                distances[from][to] = dist;
                distances[to][from] = dist;
            }

            for (int d = 0; d < 4; d++) {
                int nr = curr.r + DIRECTION[d][0];
                int nc = curr.c + DIRECTION[d][1];

                if (nr < 0 || nr >= N || nc < 0 || nc >= N) {
                    continue;
                }
                if (visited[nr][nc] != 0) {
                    continue;
                }
                if (map[nr][nc] == '1') {
                    continue;
                }

                q.add(new Point(-1, nr, nc));
                visited[nr][nc] = visited[curr.r][curr.c] + 1;
            }
        }
    }

    private static int getShortestPath() {
        PriorityQueue<Edge> edges = new PriorityQueue<>();
        for (int i = 0; i < points.size(); i++) {
            for (int j = i + 1; j < points.size(); j++) {
                if (distances[i][j] != Integer.MAX_VALUE) {
                    edges.add(new Edge(i, j, distances[i][j]));
                }
            }
        }
        int[] parent = new int[points.size()];
        for (int i = 0; i < points.size(); i++) {
            parent[i] = i;
        }
        int sum = 0;
        int connected = 0;
        while (!edges.isEmpty()) {
            Edge edge = edges.poll();
            int start = edge.start;
            int end = edge.end;

            if (find(parent, start) != find(parent, end)) {
                union(parent, start, end);
                sum += edge.weight;
                connected++;
            }
        }

        // 만약 모든 노드들을 연결하지 못했다면 -1 반환
        if (connected != points.size() - 1) {
            return -1;
        }
        return sum;
    }

    private static int find(int[] parent, int node) {
        if (parent[node] != node) {
            parent[node] = find(parent, parent[node]);
        }

        return parent[node];
    }

    private static void union(int[] parent, int node1, int node2) {
        int root1 = find(parent, node1);
        int root2 = find(parent, node2);

        if (root1 != root2) {
            parent[root1] = root2;
        }
    }

    public static void main(String[] args) {
        input();
        calculateDistances();
        int result = getShortestPath();
        System.out.println(result);
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 1528 - 금민수의 합  (0) 2024.10.26
BOJ 1655 - 가운데를 말해요  (0) 2024.10.25
BOJ 15684 - 사다리 조작  (0) 2024.10.11
BOJ 14889 - 스타트와 링크  (1) 2024.10.11
BOJ 14888 - 연산자 끼워넣기  (1) 2024.10.10

15684번: 사다리 조작 (acmicpc.net)

 

H x N 크기의 격자가 있는 것과 동일하다.

현재 세로 방향은 모든 점이 연결 돼 있는 것과 다름없다. 이제 가로를 연결해야 한다.

한 점을 선택하면 왼쪽 혹은 오른쪽의 한 점도 반드시 선택해야 한다.

그런데 다른 한 점이 이미 다른 점과 인접해 있다면 그 점은 선택이 불가능하다.

각 열에서 시작해 그래프를 탐색해 나갈 것이다. 이때 도착점의 열 번호가 시작점의 열 번호와 같게 하기 위해 가로선을 최소 몇 개 추가해야 하는가.

 

우선 대전제는 시작점에서 r이 증가해 가는 방향으로 이동하는 것이다. 그리고 r이 마지막 H에 도달하는 게 종료지점이다.

이때 현재 위치에서 좌우로 움직일 수 있다면 반드시 해당 열로 이동해야 한다.

좌우로 움직일 수 있는지의 여부는 가로선의 존재 여부이다.

기존에 존재하던 가로선 이외에 새롭게 가로선을 배치할 수 있는 곳들을 파악해야 한다.

그리고 탐색을 진행해 가는데 모든 점이 자신의 점으로 도착하면 성공 하나라도 실패하면 이제 새롭게 가로선을 배치해야 한다.

필요 기능들을 생각해 보자.

  1. 탐색하는 기능
  2. 현재 지점에서 좌우로 이동이 가능한지 확인하는 기능
  3. 가로선을 추가하는 기능

가로선을 1개 놓는 경우, 2개 놓는 경우, 3개 놓는 경우를 체크해야 한다.

이렇게 했는데도 경로를 못 찾으면 -1 반환

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

public class Main {

    static FastReader scan = new FastReader();
    static int N, M, H;
    static int WIDTH;
    static int[][] width;
    static List<int[]> widthCandidate;
    static int additionalCount = -1;

    static void input() {
        N = scan.nextInt();
        M = scan.nextInt();
        H = scan.nextInt();

        WIDTH = 2 * N - 1;
        width = new int[H][WIDTH];
        for (int i = 0; i < M; i++) {
            int r = scan.nextInt() - 1;
            int c = scan.nextInt();
            width[r][c + c - 1] = 1;
        }

        for (int c = 0; c < WIDTH; c += 2) {
            for (int r = 0; r < H; r++) {
                width[r][c] = 1;
            }
        }

        widthCandidate = new ArrayList<>();
        for (int row = 0; row < H; row++) {
            for (int col = 1; col < WIDTH; col += 2) {
                if (width[row][col] == 0) {
                    widthCandidate.add(new int[]{row, col});
                }
            }
        }
    }

    static void print() {
        for (int[] row : width) {
            System.out.println(Arrays.toString(row));
        }
        System.out.println();
    }

    static boolean addWidth(int count, int depth, int next, int[][] width) {
        if (depth == count) {
            // count개의 다리를 놨다.
            if (allPathSuccess()) return true;
            return false;
        }

        for (int i = next; i < widthCandidate.size(); i++) {
            int[] cand = widthCandidate.get(i);
            int r = cand[0];
            int c = cand[1];

            if (canAdd(r, c, width)) {
                width[r][c] = 1;
                if (addWidth(count, depth + 1, i + 1, width)) {
                    return true;
                };
                width[r][c] = 0;
            }
        }
        return false;
    }

    static boolean canAdd(int r, int c, int[][] width) {
        boolean left = true;
        boolean right = true;
        if (isValidPosition(r, c - 2)) {
            left = width[r][c - 2] != 1;
        }

        if (isValidPosition(r, c + 2)) {
            right = width[r][c + 2] != 1;
        }

        return left && right;
    }

    static boolean allPathSuccess() {
        int cnt = 0;
        for (int c = 0; c <= WIDTH; c += 2) {
            if (play(c, 0, c, new boolean[H][WIDTH])) {
                cnt++;
            }
        }
        return cnt == N;
    }

    static boolean play(int startC, int r, int c, boolean[][] visited) {
        if (r == H) {
            if (startC == c) {
                return true;
            }
            return false;
        }

        visited[r][c] = true;

        if (canMoveRight(r, c) && !visited[r][c + 2]) {
            return play(startC, r, c + 2, visited);
        } else if (canMoveLeft(r, c) && !visited[r][c - 2]) {
            return play(startC, r, c - 2, visited);
        } else {
            return play(startC, r + 1, c, visited);
        }
    }

    static boolean isValidPosition(int r, int c) {
        return r >= 0 && r < H && c >= 0 && c < WIDTH;
    }

    static boolean canMoveRight(int r, int c) {
        return isValidPosition(r, c + 1) && width[r][c + 1] == 1;
    }

    static boolean canMoveLeft(int r, int c) {
        return isValidPosition(r, c - 1) && width[r][c - 1] == 1;
    }

    public static void main(String[] args) {
        input();
        if (allPathSuccess()) {
            additionalCount = 0;
        } else {
            for (int count = 1; count <= 3; count++) {
                if (addWidth(count, 0, 0, width)) {
                    additionalCount = count;
                    break;
                }
            }
        }

        System.out.println(additionalCount);
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }

}

이렇게 풀었는데 시간초과가 발생했다.

이동해서 판정하는 로직에서 재귀를 사용함으로써 스택이 깊어져 시간 효율성이 떨어지는 문제를 해결하기 위해 반복문으로 변경했다.

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

public class Main {

    static FastReader scan = new FastReader();
    static int N, M, H;
    static int WIDTH;
    static int[][] width;
    static List<int[]> widthCandidate;
    static int additionalCount = -1;

    static void input() {
        N = scan.nextInt();
        M = scan.nextInt();
        H = scan.nextInt();

        WIDTH = 2 * N - 1;
        width = new int[H][WIDTH];
        for (int i = 0; i < M; i++) {
            int r = scan.nextInt() - 1;
            int c = scan.nextInt();
            width[r][c + c - 1] = 1;
        }

        for (int c = 0; c < WIDTH; c += 2) {
            for (int r = 0; r < H; r++) {
                width[r][c] = 1;
            }
        }

        widthCandidate = new ArrayList<>();
        for (int row = 0; row < H; row++) {
            for (int col = 1; col < WIDTH; col += 2) {
                if (width[row][col] == 0) {
                    widthCandidate.add(new int[]{row, col});
                }
            }
        }
    }

    static void print() {
        for (int[] row : width) {
            System.out.println(Arrays.toString(row));
        }
        System.out.println();
    }

    static boolean addWidth(int count, int depth, int next, int[][] width) {
        if (depth == count) {
            // count개의 다리를 놨다.
            if (allPathSuccess()) return true;
            return false;
        }

        for (int i = next; i < widthCandidate.size(); i++) {
            int[] cand = widthCandidate.get(i);
            int r = cand[0];
            int c = cand[1];

            if (canAdd(r, c, width)) {
                width[r][c] = 1;
                if (addWidth(count, depth + 1, i + 1, width)) {
                    return true;
                };
                width[r][c] = 0;
            }
        }
        return false;
    }

    static boolean canAdd(int r, int c, int[][] width) {
        boolean left = true;
        boolean right = true;
        if (isValidPosition(r, c - 2)) {
            left = width[r][c - 2] != 1;
        }

        if (isValidPosition(r, c + 2)) {
            right = width[r][c + 2] != 1;
        }

        return left && right;
    }

    static boolean allPathSuccess() {
        for (int i = 0; i < N; i++) {
            int c = i * 2; // 세로선 위치
            int currentC = c;
            for (int r = 0; r < H; r++) {
                // 오른쪽으로 이동
                if (canMoveRight(r, currentC)) {
                    currentC += 2;
                }
                // 왼쪽으로 이동
                else if (canMoveLeft(r, currentC)) {
                    currentC -= 2;
                }
                // 아래로 이동 (별도 처리 필요 없음)
            }
            if (currentC != c) {
                return false;
            }
        }
        return true;
    }

    static boolean isValidPosition(int r, int c) {
        return r >= 0 && r < H && c >= 0 && c < WIDTH;
    }

    static boolean canMoveRight(int r, int c) {
        return isValidPosition(r, c + 1) && width[r][c + 1] == 1;
    }

    static boolean canMoveLeft(int r, int c) {
        return isValidPosition(r, c - 1) && width[r][c - 1] == 1;
    }

    public static void main(String[] args) {
        input();
        for (int i = 0; i <= 3; i++) {
            if (addWidth(i, 0, 0, width)) {
                System.out.println(i);
                return;
            }
        }
        System.out.println(-1);
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }

}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 1655 - 가운데를 말해요  (0) 2024.10.25
BOJ 1944 - 복제 로봇  (2) 2024.10.23
BOJ 14889 - 스타트와 링크  (1) 2024.10.11
BOJ 14888 - 연산자 끼워넣기  (1) 2024.10.10
BOJ 14503 - 로봇 청소기  (2) 2024.10.10

14889번: 스타트와 링크 (acmicpc.net)

 

능력치의 차이를 최소화.

분할된 팀의 능력치를 구할 수 있어야 한다.

우선 N 명을 N/2 명 씩 두 팀으로 나눠야 한다.

그 후 각 팀의 점수를 계산해서 차이를 구한 다음 갱신을 한다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Main {

    static FastReader scan = new FastReader();
    static int N;
    static int[][] status;
    static int minGap = Integer.MAX_VALUE;

    static void input() {
        N = scan.nextInt();
        status = new int[N][N];

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                status[i][j] = scan.nextInt();
            }
        }
    }

    static void recFunc(int depth, int[] team, boolean[] visited) {
        if (depth >= N/2) {
            Set<Integer> teamA = Arrays.stream(team).boxed().collect(Collectors.toSet());
            Set<Integer> teamB = IntStream.range(0, N)
                    .boxed()
                    .collect(Collectors.toSet());
            teamB.removeAll(teamA);
            calculate(teamA, teamB);
            return;
        }

        for (int i = depth == 0 ? 0 : team[depth - 1]; i < N; i++) {
            if (visited[i]) continue;
            team[depth] = i;
            visited[i] = true;
            recFunc(depth + 1, team, visited);
            team[depth] = 0;
            visited[i] = false;
        }
    }

    static void calculate(Set<Integer> teamA, Set<Integer> teamB) {
        int teamAScore = calculateTeamScore(teamA);
        int teamBScore = calculateTeamScore(teamB);
        minGap = Math.min(minGap, Math.abs(teamAScore - teamBScore));
    }

    static int calculateTeamScore(Set<Integer> team) {
        int score = 0;

        for (int user1 : team) {
            for (int user2 : team) {
                if (user1 == user2) continue;
                score += status[user1][user2];
            }
        }
        return score;
    }

    public static void main(String[] args) {
        input();
        recFunc(0, new int[N/2], new boolean[N]);
        System.out.println(minGap);
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

그런데 이제 보니까 teamA와 teamB의 Set은 필요가 없다. 이미 visited에서 방문한 사람들을 체크하고 있기 때문에 해당 배열이 true이면 A팀 false면 B팀으로 처리하면 된다. 그냥 팀을 나눠야 한다는 생각에 쓸모없는 시간과 공간을 낭비했다.

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

public class Main {

    static FastReader scan = new FastReader();
    static int N;
    static int[][] status;
    static int minGap = Integer.MAX_VALUE;

    static void input() {
        N = scan.nextInt();
        status = new int[N][N];

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                status[i][j] = scan.nextInt();
            }
        }
    }

    static void recFunc(int depth, int[] team, boolean[] visited) {
        if (depth >= N/2) {
            int scoreGap = calculateTeamScoreGap(visited);
            minGap = Math.min(minGap, scoreGap);
            return;
        }

        for (int i = depth == 0 ? 0 : team[depth - 1]; i < N; i++) {
            if (visited[i]) continue;
            team[depth] = i;
            visited[i] = true;
            recFunc(depth + 1, team, visited);
            team[depth] = 0;
            visited[i] = false;
        }
    }

    static int calculateTeamScoreGap(boolean[] visited) {
        int score = 0;

        for (int i = 0; i < N; i++) {
            for (int j = i + 1; j < N; j++) {
                if (visited[i] && visited[j]) {
                    score += status[i][j] + status[j][i];
                } else if (!visited[i] && !visited[j]) {
                    score -= status[i][j] + status[j][i];
                }
            }
        }
        return Math.abs(score);
    }

    public static void main(String[] args) {
        input();
        recFunc(0, new int[N/2], new boolean[N]);
        System.out.println(minGap);
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 1944 - 복제 로봇  (2) 2024.10.23
BOJ 15684 - 사다리 조작  (0) 2024.10.11
BOJ 14888 - 연산자 끼워넣기  (1) 2024.10.10
BOJ 14503 - 로봇 청소기  (2) 2024.10.10
BOJ 14502 - 연구소  (0) 2024.10.10

14888번: 연산자 끼워넣기 (acmicpc.net)

 

연산자 4개의 개수가 주어진다. 수가 N개 있으니 N - 1개의 위치에 연산자들을 배치하면 된다.

재귀를 통해 연산자를 선택하고 결과를 누적해 간다.

모든 연산자 선택이 끝나고 결과가 완성되면 최대, 최소를 갱신한다.

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

public class Main {

    static FastReader scan = new FastReader();
    static int N;
    static int[] nums;
    static int[] ops;
    static int MIN = Integer.MAX_VALUE;
    static int MAX = Integer.MIN_VALUE;

    static void dfs(int depth, int sum) {
        if (depth == N - 1) {
            MAX = Math.max(MAX, sum);
            MIN = Math.min(MIN, sum);
            return;
        }

        for (int i = 0; i < 4; i++) {
            if (ops[i] > 0) {
                ops[i]--;
                dfs(depth + 1, calculate(sum, i, nums[depth + 1]));
                ops[i]++;
            }
        }
    }

    static int calculate(int num1, int op, int num2) {
        if (op == 0) {
            return num1 + num2;
        } else if (op == 1) {
            return num1 - num2;
        } else if (op == 2) {
            return num1 * num2;
        } else {
            if (num1 < 0) {
                num1 = -num1;
                return -num1 / num2;
            } else {
                return num1 / num2;
            }
        }
    }

    static void input() {
        N = scan.nextInt();
        nums = new int[N];
        ops = new int[4];

        for (int i = 0; i < N; i++) {
            nums[i] = scan.nextInt();
        }

        for (int i = 0; i < 4; i++) {
            ops[i] = scan.nextInt();
        }
    }

    public static void main(String[] args) {
        input();
        dfs(0, nums[0]);
        System.out.println(MAX);
        System.out.println(MIN);
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 15684 - 사다리 조작  (0) 2024.10.11
BOJ 14889 - 스타트와 링크  (1) 2024.10.11
BOJ 14503 - 로봇 청소기  (2) 2024.10.10
BOJ 14502 - 연구소  (0) 2024.10.10
BOJ 14501 - 퇴사  (1) 2024.10.09

14503번: 로봇 청소기 (acmicpc.net)

 

로봇 청소기의 이동 = 바라보는 방향이 존재

처음 빈칸은 전부 청소되지 않은 상태

  1. 현재 칸이 청소되지 않은 경우 현재 칸을 청소
  2. 현재 칸의 주변 4칸 중 청소되지 않은 빈칸이 없는 경우
    1. 바라보는 방향을 유지한 채로 한 칸 후진할 수 있다면 한 칸 후진하고 1번으로
    2. 바라보는 방향의 뒤쪽 칸이 벽이라 후진할 수 없다면 작동을 멈춘다.
  3. 현재 칸의 주변 4칸 중 청소되지 않은 빈 칸이 있는 경우
    1. 반시계 방향으로 90도 회전한다.
    2. 바라보는 방향을 기준으로 앞쪽이 청소되지 않은 빈칸인 경우 한 칸 전진한다.
    3. 1번으로 돌아간다.

 

필요한 기능을 정리해보자.

  1. 현재 위치의 청소 여부 판단
  2. 주변 4칸의 청소 여부 판단
  3. 후진
  4. 전진
  5. 반시계 90도 회전
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

    static FastReader scan = new FastReader();
    static int[][] DIRECTION = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}}; // 북서남동
    static int N, M;
    static Robot robot;
    static int[][] map;

    static class Robot {
        int r;
        int c;
        int d;

        public Robot(int r, int c, int d) {
            this.r = r;
            this.c = c;
            this.d = d;
        }

        void moveForward() {
            r += DIRECTION[d][0];
            c += DIRECTION[d][1];
        }

        void moveBack() {
            r -= DIRECTION[d][0];
            c -= DIRECTION[d][1];
        }

        void rotate() {
            d = (d + 1) % 4;
        }
    }

    static class Room {
        int[][] map;
        Robot robot;
        int cleanCount;

        public Room(int[][] map, Robot robot) {
            this.map = map;
            this.robot = robot;
        }

        public void clean() {
            while (true) {
                if (map[robot.r][robot.c] == 0) {
                    map[robot.r][robot.c] = 2;
                    cleanCount++;
                }

                // 주변 4칸을 탐색
                if (existUncleanRoom(map, robot.r, robot.c)) {
                    // 4칸 중 청소되지 않은 빈 칸이 존재한다.
                    for (int i = 0; i < 4; i++) {
                        robot.rotate();
                        int nr = robot.r + DIRECTION[robot.d][0];
                        int nc = robot.c + DIRECTION[robot.d][1];
                        if (nr < 0 || nr >= N || nc < 0 || nc >= M) {
                            continue;
                        }
                        if (map[nr][nc] == 0) {
                            robot.moveForward();
                            break;
                        }
                    }

                } else {
                    // 4방향 다 봤는데 청소되지 않은 칸이 없다.
                    robot.moveBack();
                    if (map[robot.r][robot.c] == 1) {
                        // 후진했는데 벽인 경우 종료
                        return;
                    }
                }
            }
        }

        private boolean existUncleanRoom(int[][] map, int r, int c) {
            for (int d = 0; d < 4; d++) {
                if (map[r + DIRECTION[d][0]][c + DIRECTION[d][1]] == 0) {
                    return true;
                }
            }
            return false;
        }

        public int getCleanCount() {
            return cleanCount;
        }
    }

    static void input() {
        N = scan.nextInt();
        M = scan.nextInt();
        map = new int[N][M];

        // 로복 좌표, 방향
        int r = scan.nextInt();
        int c = scan.nextInt();
        int d = scan.nextInt();
        if (d == 0) {
            // 북
            d = 0;
        } else if (d == 1) {
            // 동
            d = 3;
        } else if (d == 2) {
            // 남
            d = 2;
        } else {
            //서
            d = 1;
        }

        robot = new Robot(r, c, d);
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                map[i][j] = scan.nextInt(); // 0 빈칸 1 벽
            }
        }
    }

    public static void main(String[] args) {
        input();
        Room room = new Room(map, robot);
        room.clean();
        System.out.println(room.getCleanCount());
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 14889 - 스타트와 링크  (1) 2024.10.11
BOJ 14888 - 연산자 끼워넣기  (1) 2024.10.10
BOJ 14502 - 연구소  (0) 2024.10.10
BOJ 14501 - 퇴사  (1) 2024.10.09
BOJ 14500 - 테트로미노  (1) 2024.10.09

+ Recent posts