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

 

16234번: 인구 이동

N×N크기의 땅이 있고, 땅은 1×1개의 칸으로 나누어져 있다. 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고 있다. 인접한 나라 사이에는 국경선이 존재한다. 모

www.acmicpc.net

 

[정답 코드]

import java.io.*;
import java.util.*;

public class Main {
    static int[][] graph;
    static boolean[][] visited;
    static int[] dx = {-1, 1, 0, 0};
    static int[] dy = {0, 0, -1, 1}; // 상하좌우순서
    static int N, L ,R;
    static ArrayList<int[]> list;
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

        N = Integer.parseInt(st.nextToken());
        L = Integer.parseInt(st.nextToken());
        R = Integer.parseInt(st.nextToken());

        graph = new int[N][N];
        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine(), " ");
            for (int j = 0; j < N; j++) {
                graph[i][j] = Integer.parseInt(st.nextToken());
            }
        }
        System.out.println(move());
    }

    public static int move() { // 더이상 인구 이동이 일어나지 않을때 까지 반복
        int result = 0;
        while (true) {
            boolean isMove = false;
            visited = new boolean[N][N];
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    if (!visited[i][j]) {
                        int sum = dfs(i, j); // 방문하지 않은 곳 dfs 탐색
                        if (list.size() > 1) {
                            changePopulation(sum); // 국경이 열린 노드끼리 인구 이동
                            isMove = true;
                        }
                    }
                }
            }
            if (!isMove) {
                return result;
            }
            result++;
        }
    }

    public static void changePopulation(int sum) {
        int avg = sum / list.size();
        for (int i = 0; i < list.size(); i++) {
            int x = list.get(i)[0];
            int y = list.get(i)[1];
            graph[x][y] = avg;
        }
    }

    public static int dfs(int x, int y) {
        Queue<int[]> queue = new LinkedList<>();
        queue.offer(new int[]{x, y});

        list = new ArrayList<>();
        list.add(new int[]{x, y});

        visited[x][y] = true;
        int sum = graph[x][y];

        while (!queue.isEmpty()) {
            int tmp[] = queue.poll();
            for (int i = 0; i < 4; i++) { // 4방탐색
                int nx = tmp[0] + dx[i];
                int ny = tmp[1] + dy[i];
                if (nx >= 0 && nx < N && ny >= 0 && ny < N) { // graph 범위 내
                    // 방문한적 없고, L과 R 사이일 때
                    if (!visited[nx][ny] && L <= Math.abs(graph[tmp[0]][tmp[1]] - graph[nx][ny]) && Math.abs(graph[tmp[0]][tmp[1]] - graph[nx][ny]) <= R) {
                        visited[nx][ny] = true; // 방문한곳으로 변경
                        queue.offer(new int[]{nx, ny}); // 새로 큐에 담아줌
                        list.add(new int[]{nx, ny}); // 연결된 나라끼리는 따로 좌표를 담아줌
                        sum += graph[nx][ny];
                    }
                }
            }
        }
        return sum;
    }
}

 

[설명]

dfs와 구현을 동시에 해야하는 문제이다. 

문제를 세분화해서 풀어야 한다.

1. 순회를하며 방문하지 않은 노드를방문한다. 모든 노드를방문할 때 까지 반복된다.
2. 방문한 노드에서 dfs 알고리즘을 구현. 

  • dfs 알고리즘 : 연결된 노드들의 좌표를 list에 담아줌. 연결된 노드들의 값을 모두 더함.

3. 연결된 것 노드의 체크가 끝나면 인구 이동을 시작.

  • 인구이동 로직 : 연결된 노드들의 좌표를 담았던 list를 하나씩 꺼내며 연결된 노드들의 값을 모드 더했던것을 적절히 나눠 값을 배분

4. 모든 노드들을 방문했다면 1일 증가

5. 1 ~ 5를 반복, 인구이동이 더이상 일어나지 않을 때 까지 반복

'코딩테스트' 카테고리의 다른 글

백준 1068번: 트리[JAVA]  (1) 2024.03.15
백준 1991번: 트리 순회[JAVA]  (1) 2024.03.15
백준 2493번: 탑[JAVA]  (0) 2024.03.12
백준 7682번: 틱택토[JAVA]  (0) 2024.03.11
백준 9372번: 상근이의 여행[JAVA]  (0) 2024.03.09

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

 

2493번: 탑

첫째 줄에 탑의 수를 나타내는 정수 N이 주어진다. N은 1 이상 500,000 이하이다. 둘째 줄에는 N개의 탑들의 높이가 직선상에 놓인 순서대로 하나의 빈칸을 사이에 두고 주어진다. 탑들의 높이는 1

www.acmicpc.net

[정답 코드]

import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

        int N = Integer.parseInt(st.nextToken());
        Stack<int[]> stack = new Stack<>();
        st = new StringTokenizer(br.readLine(), " ");
        for (int i = 1; i <= N; i++) { // 탑은 0번부터가 아닌 1번부터 시작
            int top = Integer.parseInt(st.nextToken()); // 현재 확인중인 탑
            while (!stack.isEmpty()) {
                if (stack.peek()[1] >= top) {
                    bw.write(stack.peek()[0] + " ");
                    break;
                }
                stack.pop();
            }
            if (stack.isEmpty()) {
                bw.write("0 ");
            }
            stack.push(new int[]{i, top});
        }
        bw.flush();
        bw.close();
        br.close();
    }
}

 

[설명]

제일 왼쪽 탑부터 스택에 담긴 값들과 비교해가면 로직을 반복한다.

스택에는 높이가 작은 순서부터 큰순으로 오름차순으로값이 담기게 된다.

 

높은 6탑은 첫번째 탑이기 때문에 비교할 스택들이 없어 0을 bw.write 한 후 {탑의 위치, 탑의 높이}를 스택에 담는다.

 

높이 9탑은 스택에 담긴 6보다 높이가 높다. 그 뜻은 앞으로 확인할 탑들은 모두 높이 9 탑 때문에 스택에 담겼던 높이 6 탑을 만날 수 없다. 그러므로 높이 6 스택은 pop해준다. 높이 9 탑을 스택에 담는다.

 

높이 5탑과 스택에 담긴 높이9 탑과 비교해준다. 높이9 탑에 신호가 닿기 때문에 높이9의 위치를 출력해준 후 스택에 높이 5 스택을 담아준다.

 

높이 7탑과 스택에 담긴값과 비교해준다. 스택의 가장 위의 값인 높이 5와 비교를 해본다. 스택에 담겼던 스택 5탑은 현재 확인중인 높이 7탑 때문에 앞으로 모든신호를 못받게된다. 그러므로 스택에서 pop 해준다. 그 다음 스택에 담긴 값 높이 9랑 비교 하면 신호가 9에 닿는다. 9의 위치를 출력후 높이7 탑을 스택에 담는다.

 

높이 4탑과 스택에 담긴 값을 비교해본다. 스택의 top에 있는값 높이 7인탑과 비교해보니 바로 신호가 닿는다. 높이 7탑의 위치를 출력해준다.

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

 

7682번: 틱택토

입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 줄은 9개의 문자를 포함하며, 'X', 'O', '.' 중 하나이다. '.'은 빈칸을 의미하며, 9개의 문자는 게임판에서 제일 윗 줄 왼쪽부터의 순서이다. 입

www.acmicpc.net

 

[정답 코드]

import java.io.*;
import java.util.*;

public class Main {
    static char[][] board;
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st;
        String input = "";
        board = new char[3][3];
        while (true) {
            st = new StringTokenizer(br.readLine(), " ");
            input = st.nextToken();
            if (Objects.equals(input, "end")) {
                break;
            }
            int index = 0;
            int xCount = 0;
            int oCount = 0;
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    board[i][j] = input.charAt(index);
                    if (board[i][j] == 'O') {
                        oCount++;
                    } else if (board[i][j] == 'X') {
                        xCount++;
                    }
                    index++;
                }
            }
            //게임판이 꽉 채워졌을 때
            //X가 먼저 말을 놓았음으로 O보다 1개 무조건 많아야 한다.
            if (xCount + oCount == 9 && xCount - oCount == 1) {
                //한 명이 빙고를 완성하면 게임이 끝나기 때문에
                //둘 다 빙고가 성립될 수 없다.
                if (isValid('X') && isValid('O')) {
                    bw.write("invalid\n");
                } else if (isValid('O')) {//말이 꽉 채워진 경우에는 O가 이길 수 없다
                    bw.write("invalid\n");
                } else { // X 가 이긴경우
                    bw.write("valid\n");
                }
            } else { // 게임판이 꽉 채워지지 않았을 경우
                //한 명이 빙고를 완성하면 게임이 끝나기 때문에
                //둘 다 빙고가 성립될 수 없다.
                if (isValid('X') && isValid('O')) {
                    bw.write("invalid\n");
                } else if (isValid('X') && xCount - oCount == 1) { //X가 이겼을 땐, X가 선공이어서 무조건 O보다 하나 많아야 한다
                    bw.write("valid\n");
                } else if (isValid('O') && xCount == oCount) { //O가 이겼을 땐, O가 후공이여서 X와 O의 개수가 같아야 한다
                    bw.write("valid\n");
                } else { // 아직 게임판이 덜채워졌는데 게임이 끝날 수는 없다
                    bw.write("invalid\n");
                }
            }
        }
        bw.flush();
        bw.close();
        br.close();
    }

    public static boolean isValid(char c) {
        //가로가 성립할 때
        for (int i = 0; i < 3; i++) {
            int count = 0;
            for (int j = 0; j < 3; j++) {
                if (board[i][j] == c) {
                    count++;
                }
            }
            if (count == 3) {
                return true;
            }
        }

        //세로가 성립할 때
        for (int i = 0; i < 3; i++) {
            int count = 0;
            for (int j = 0; j < 3; j++) {
                if (board[j][i] == c) {
                    count++;
                }
            }
            if (count == 3) {
                return true;
            }
        }

        // 대각선이 성립할 때
        if (board[0][0] == c && board[1][1] == c && board[2][2] == c) {
            return true;
        }
        if (board[0][2] == c && board[1][1] == c && board[2][0] == c) {
            return true;
        }

        return false;
    }
}

 

[설명]

단순 구현 문제이다. 크게 봤을 때는 게임판에 말이 가득 찼을 경우와 가득차지 않았을 경우로 나눌수 있다.

 

  1. 말이 가득찼을 경우(X가 무조건 O보다 한개 많다)
    • X와 O둘다 동시에 이길 수는 없다
    • O가 이길수는 없다
    • X가 이긴다
    • 둘다 못 이긴다
  2. 말이 가득차지 않았을 경우
    • X와 O둘다 동시에 이길 수는 없다
    • X가 이겼을 땐 X가 선공이라 X가 O보다 1개 많아야 한다
    • O가 이겼을 땐 O가 후공이라 X와 O는 같아야 한다
    • 아직 게임판이 덜 채워졌는데 게임이 끝날 수는 없다

 

'코딩테스트' 카테고리의 다른 글

백준 16234번: 인구 이동[JAVA]  (1) 2024.03.13
백준 2493번: 탑[JAVA]  (0) 2024.03.12
백준 9372번: 상근이의 여행[JAVA]  (0) 2024.03.09
백준 2668번: 숫자고르기[JAVA]  (0) 2024.03.07
백준 5972번: 택배 배송[JAVA]  (0) 2024.03.07

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

 

9372번: 상근이의 여행

첫 번째 줄에는 테스트 케이스의 수 T(T ≤ 100)가 주어지고, 각 테스트 케이스마다 다음과 같은 정보가 주어진다. 첫 번째 줄에는 국가의 수 N(2 ≤ N ≤ 1 000)과 비행기의 종류 M(1 ≤ M ≤ 10 000) 가

www.acmicpc.net

 

[정답 코드]

import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

        int T = Integer.parseInt(st.nextToken());
        for (int i = 0; i < T; i++) {
            st = new StringTokenizer(br.readLine(), " ");
            int N = Integer.parseInt(st.nextToken());
            int M = Integer.parseInt(st.nextToken());
            for (int j = 0; j < M; j++) {
                st = new StringTokenizer(br.readLine(), " ");
            }
            System.out.println(N - 1);
        }
    }
}

 

[설명]

문제를 풀면서 뭔가 좀 이상함을 느꼈었다. 문제가 이해하기 어렵게 적혀있지만 결국에는 노드와 간선의 관계를 물어보는 문제였다. 노드가 N개 있고 노드끼리 모드 연결되어있게하려면 간선은 최소 N - 1개 있어야한다는걸 알고있냐는 질문이였다. 비행기를 타는 최소 횟수 같은걸 물어보는거였다면 문제를 다르게 풀어야 했겠지만 그냥 비행기의 종류가 몇개냐는 물어보는거였기 때문에 정답 코드가 이렇다.

'코딩테스트' 카테고리의 다른 글

백준 2493번: 탑[JAVA]  (0) 2024.03.12
백준 7682번: 틱택토[JAVA]  (0) 2024.03.11
백준 2668번: 숫자고르기[JAVA]  (0) 2024.03.07
백준 5972번: 택배 배송[JAVA]  (0) 2024.03.07
백준 5972번: 택배 배송[JAVA]  (0) 2024.03.05

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

 

2668번: 숫자고르기

세로 두 줄, 가로로 N개의 칸으로 이루어진 표가 있다. 첫째 줄의 각 칸에는 정수 1, 2, …, N이 차례대로 들어 있고 둘째 줄의 각 칸에는 1이상 N이하인 정수가 들어 있다. 첫째 줄에서 숫자를 적절

www.acmicpc.net

 

[정답 코드]

import java.io.*;
import java.util.*;

public class Main {
    static int n, num;
    static int[] arr;
    static boolean[] check;
    static List<Integer> answer;
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
//        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

        int N = Integer.parseInt(br.readLine());
        arr = new int[N];
        check = new boolean[N];
        for (int i = 0; i < N; i++) {
            arr[i] = Integer.parseInt(br.readLine()) - 1;
        }
        answer = new LinkedList<>(); // 답은 가변적이므로 리스트로 선언

        for (int i = 0; i < N; i++) {
            check[i] = true;
            num = i;
            dfs(i);
            check[i] = false;
        }
        Collections.sort(answer);
        System.out.println(answer.size());
        for (Integer integer : answer) {
            System.out.println(integer + 1);
        }
    }

    public static void dfs(int i) {
        if (arr[i] == num) { // 한바퀴돌아 다시 제자리로 돌아오는지 체크
            answer.add(num); // 돌아온다면 정답에 추가
        }
        if (!check[arr[i]]) {
            check[arr[i]] = true;
            dfs(arr[i]);
            check[arr[i]] = false;
        }
    }
}

 

[설명]

난이도가 높아질수록 문제를 이해하는것 자체부터가 힘들어진다. for문을 N번 만큼 돌면서 dfs 로직을 실행시켜준다.

dfs로직은 출발 숫자 -> arr[출발 숫자] -> arr[arr[출발 숫자]] 를 반복하다 출발 숫자를 다시 만나게 되면 첫째 줄과 둘째 줄 정수들의 집합이 일치한다고 볼 수 있다. 

for문을 N번 만큼 실행시키는 이유는 최대값을 찾기 위해서이다.

 

'코딩테스트' 카테고리의 다른 글

백준 7682번: 틱택토[JAVA]  (0) 2024.03.11
백준 9372번: 상근이의 여행[JAVA]  (0) 2024.03.09
백준 5972번: 택배 배송[JAVA]  (0) 2024.03.07
백준 5972번: 택배 배송[JAVA]  (0) 2024.03.05
백준 14719번: 빗물[JAVA]  (0) 2024.03.05

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

 

2467번: 용액

첫째 줄에는 전체 용액의 수 N이 입력된다. N은 2 이상 100,000 이하의 정수이다. 둘째 줄에는 용액의 특성값을 나타내는 N개의 정수가 빈칸을 사이에 두고 오름차순으로 입력되며, 이 수들은 모두 -

www.acmicpc.net

 

[정답 코드]

import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

        int N = Integer.parseInt(st.nextToken());
        int[] arr = new int[N];

        st = new StringTokenizer(br.readLine(), " ");
        for (int i = 0; i < N; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
        }

        int left = 0;
        int right = N - 1;
        int min = Integer.MAX_VALUE; // 현재 알고있는 최솟값
        int[] answer = new int[2];
        while (left < right) {
            int sum = Math.abs(arr[left] + arr[right]); // 새로운 최솟값
            if (sum < min) { // 새로운 최솟값이 원래 알고있는 최솟값보다 더 작다면 갱신
                min = sum;
                answer[0] = arr[left];
                answer[1] = arr[right];
            }
            if (sum == 0) {
                break;
            }
            if (arr[left] + arr[right] > 0) {
                right--;
            } else {
                left++;
            }
        }
        System.out.println(answer[0] + " " + answer[1]);
    }
}

 

[설명]

간단한 이분탐색 알고리즘 문제이다. 다른 이분탐색 알고리즘과 다른점은 left 와 right 가 arr배열의 인덱스 내에서만 이루어져야만 한다는것을 조심해야한다.

left는 0 (첫번째 인덱스), right는 N - 1 (마지막 인덱스)로 초기화하고 left가 right를 넘어갈때 까지 반복한다.

left 와 right를 더한 값이 0 이상일 경우 right-- 아닐 경우 left ++ 한다.

혹은 0 일경우 바로 출력한다.

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

 

5972번: 택배 배송

농부 현서는 농부 찬홍이에게 택배를 배달해줘야 합니다. 그리고 지금, 갈 준비를 하고 있습니다. 평화롭게 가려면 가는 길에 만나는 모든 소들에게 맛있는 여물을 줘야 합니다. 물론 현서는

www.acmicpc.net

 

[정답 코드]

import java.io.*;
import java.util.*;

public class Main {
    public static int N, M;
    public static ArrayList<ArrayList<Node>> graph = new ArrayList<>(); // 2차원 ArrayList 로 graph 를 구현(2차원 배열처럼)
    public static boolean[] visited;
    public static int[] d = new int[50001];
    public static int answer = 0;
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

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

        visited = new boolean[N];

        //ArrayList 초기화
        for(int i=0;i<=N;i++) {
            graph.add(new ArrayList<>());
        }

        for(int i=0;i<M;i++) {
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken()); // 시작 노드
            int b = Integer.parseInt(st.nextToken()); // 끝 노드
            int dist = Integer.parseInt(st.nextToken()); // 비용

            // 양방향 간선
            graph.get(a).add(new Node(b, dist)); // a 에서 b 까지의 가중치 dist
            graph.get(b).add(new Node(a, dist)); // b 에서 a 까지의 가중치 dist
        }

        //1부터 N 까지의 최소거리 구하기
        dijkstra(1);

        System.out.println(d[N]);
    }

    public static void dijkstra(int start) {
        Arrays.fill(d, Integer.MAX_VALUE); // d 배열의 모든 요소를 Integer.MAX_VALUE 로 채워줌

        PriorityQueue<Node> pq = new PriorityQueue<>();
        pq.offer(new Node(start, 0)); // 시작노드에서 시작노드로 가기위한 최단 경로는 0
        d[start] = 0; // 시작노드에서 시작노드까지의 가중치는 제자리 이므로 0

        while (!pq.isEmpty()) {
            Node temp = pq.poll();
            int nodeB = temp.nodeB; // 현재 노드번호
            int distance = temp.distance; // 현재 노드의 가중치

            if(d[nodeB] < distance) continue; // //현재 노드가 이미 처리된적이 있는 노드라면 무시 ( 거리가 더 작다는것은 이미 더 효율적인 방안으로 처리된 것 )
            for(int i=0;i<graph.get(nodeB).size();i++) { //현재 노드와 연결된 다른 인접한 노드들을 확인
                int cost = d[nodeB] + graph.get(nodeB).get(i).distance; // cost = 현재노드의 가중치 + 현재노드와 다음 노드까지의 간선의 가중치
                if( cost < d[graph.get(nodeB).get(i).nodeB]) { // 새롭게 측정한 비용이 다음 노드가 원래 가지고 있던 가중치보다 적을 경우
                    d[graph.get(nodeB).get(i).nodeB] = cost; // 더 적은 값으로 갱신해준다
                    pq.offer(new Node( graph.get(nodeB).get(i).nodeB, cost)); // 계속 탐색을 해봐야 모든 노드들의 최솟값을 알 수 있기 때문에 현재의 가중치를 가지고 계속 탐색
                }
            }
        }
    }

}


// 그래프의 간선을 표현하기 위한 클래스
class Node implements Comparable<Node>{
    int nodeB; // 이 간선이 연결하는 목적지 노드의 번호
    int distance; // 시작노드에서 nodeB 까지의 가중치
    public Node(int nodeB, int distance) {
        this.nodeB = nodeB;
        this.distance = distance;
    }

    // Node 인스턴스 끼리 비교할 때 사용
    // 우선순위 큐에 삽입되었을 때 distance 값이 작은 노드(가장 가까운 노드) 부터 우선순위가 높게 설정되어 먼저 처리된다
    @Override
    public int compareTo(Node other) {
        if(this.distance > other.distance) {
            return 1;
        }else if(this.distance == other.distance) {
            return 0;
        }else {
            return -1;
        }
    }
}

 

[설명]

이 문제는 다익스트라 알고리즘을 활용해 푸는 기본적인 문제이다. 코드는 다른사람의 코드를 참고했다. 

graph 를 이중 ArrayList로 선언해주고 노드끼리의 가중치를 초기화 해준다. 

graph는 이런형태로 값이 저장된다. 

코드에 주석을 자세히 달아 놨으므로 코드와 주석을 동시에 보면서 확인하면 이해가 쉽다.

'코딩테스트' 카테고리의 다른 글

백준 2668번: 숫자고르기[JAVA]  (0) 2024.03.07
백준 5972번: 택배 배송[JAVA]  (0) 2024.03.07
백준 14719번: 빗물[JAVA]  (0) 2024.03.05
백준 14940번: 쉬운 최단거리[JAVA]  (1) 2024.02.29
백준 1072번: 게임[JAVA]  (0) 2024.02.15

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

 

14719번: 빗물

첫 번째 줄에는 2차원 세계의 세로 길이 H과 2차원 세계의 가로 길이 W가 주어진다. (1 ≤ H, W ≤ 500) 두 번째 줄에는 블록이 쌓인 높이를 의미하는 0이상 H이하의 정수가 2차원 세계의 맨 왼쪽 위치

www.acmicpc.net

 

 

[정답 코드]

 

import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

        int H = Integer.parseInt(st.nextToken());
        int W = Integer.parseInt(st.nextToken());
        int[] arr = new int[W];

        st = new StringTokenizer(br.readLine(), " ");
        for (int i = 0; i < W; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
        }

        int answer = 0;

        for (int i = 1; i < W - 1; i++) { // 처음벽과 마지막벽에는 물이 고일수 없다
            int current = arr[i]; // 현재 벽의 높이
            int leftMax = current; // 왼쪽 벽
            int rightMax = current; // 오른쪽 벽
            for (int j = i - 1; j >= 0; j--) { // 왼쪽 벽 최대 높이 탐색
                if (arr[j] > current) {
                    leftMax = Math.max(leftMax, arr[j]);
                }
            }
            for (int j = i + 1; j < W; j++) { // 오른쪽 벽 최대 높이 탐색
                if (arr[j] > current) {
                    rightMax = Math.max(rightMax, arr[j]);
                }
            }
            if (Math.min(leftMax, rightMax) > current) { // 현재 벽보다 높은 벽이 양쪽에 있는 경우
                answer += (Math.min(leftMax, rightMax) - arr[i]);
            }
        }
        System.out.println(answer);
    }
}

 

[설명]

처음에는 구현문제가 쉬운 문제인줄 알았는데 풀면 풀 수록 구현 문제도 어렵다는걸 깨달았다. 이 문제는 다른 블로그들을 대부분 참고했다.

문제 풀이방법은 for문을 돌며 현재 벽 기준으로 왼쪽 벽 중 가장 높은벽과 가장 높은 오른쪽 벽을 구한 후, 그 벽들이 현재 벽보다 높을 경우 빗물이 고이기 때문에 높은 벽에서 현재 벽을 빼주면 그 차이 만큼 빗물이 쌓인다.

이때 문제의 핵심은 두가지가 있다.

1. 가장 왼쪽벽과 가장 오른쪽 벽에는 빗물이 고일 수 없다.

그래서 for문을 돌릴때 i = 1부터 시작되고 끝은 W - 1까지 한다. 

2. 현재 벽보다 높은 양쪽 벽중에 더 낮은 벽 기준으로 빗물이 고인다.

예를 들면 빨간 체크한 벽이 for문에서 현재 current 라고 했을 때, 왼쪽벽 중 가장 높은벽은 4 이고 오른쪽벽 중  가장 높은 벽은 2이다. 이때 그림으로 보면 알 수 있듯이 왼쪽벽을 기준으로 계산하는게 아닌 오른쪽 벽을 기준으로 계산해야 한다.

'코딩테스트' 카테고리의 다른 글

백준 5972번: 택배 배송[JAVA]  (0) 2024.03.07
백준 5972번: 택배 배송[JAVA]  (0) 2024.03.05
백준 14940번: 쉬운 최단거리[JAVA]  (1) 2024.02.29
백준 1072번: 게임[JAVA]  (0) 2024.02.15
백준 1920번: 수 찾기[JAVA]  (0) 2024.02.14

+ Recent posts