문제                                                     

 

좌측, 전위순회 순서대로 입력 될 값 / 우측, 좌측의 값을 토대로 구성된 이진트리

 

이진탐색트리(자식 노드의 규칙,

현재 노드의 좌측 자식 노드는 현재 노드 보다 낮다, 

현재 노드의 우측 자식 노드는 현재 노드 보다 높다) 가 전위순회(현재 노드, 좌측 노드, 우측 노드 순)로 입력 된다.

이를 후위순회하여 출력하는 문제이다.

 

내 풀이
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    static class Node {
        int num;
        Node left;
        Node right;

        public Node(int num) {
            this.num = num;
        }

        public void insert(int n) {
            if (n < this.num) {
                if (this.left == null) {
                    this.left = new Node(n);
                } else this.left.insert(n);
            } else {
                if (this.right == null) {
                    this.right = new Node(n);
                } else this.right.insert(n);
            }
        }
    }
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        Node root = new Node(Integer.parseInt(br.readLine()));

        String input;
        while (true) {
            input = br.readLine();

            if (input == null) 
                break;
            
            root.insert(Integer.parseInt(input));
        }
        postOrder(root);
    }
    static void postOrder(Node node) {
        if (node == null) {
            return;
        }
        postOrder(node.left);
        postOrder(node.right);
        System.out.println(node.num);
    }
}

 

리뷰
  • class Node
    • 기본 노드 클래스를 만들어 key값인 num과 left, right를 활용한다.
  • Method insert
    • 노드 클래스를 활용해 입력 받는 값을 조건에 맞춰 tree를 만들어 낸다.
  • 입력 방법
    • BufferdReader를 이용해 첫 root 값을 미리 생성
      • Node root = new Node(Integer.parseInt(br.readLine()));
    • while 문을 돌며 입력 값이 없을 때 까지 값을 넣는다.
      • 문제에서 노드의 수가 절대값으로 정해져있지 않기 때문에 while문을 활용한다.
      • break 조건, if (input == null) 을 활용해 입력 값이 없을 때 까지 받아낸다.
        • input.isEmpty()를 활용했을 경우 런타임 익셉션을 경험했다.
        • BufferedReader의 readLine 메서드는 읽을 값이 없을 때 null을 반환한다. 때문에 isMepty()를 활용할 경우 null 값은 값이 비어있는 상태가 아니기 때문에 런타임 익셉션이 발생한다.
  • 생성 된 root를 최종적으로 postOrder 메서드를 이용해 출력해 낸다.
  • Method postOrder(좌측노드, 우측 노드, 현재 노드 순)
    • 좌측, 우측, 현재 노드 순으로 재귀적으로 출력해 낸다.
      • 재귀함수인 이유로 return 조건 node == null을 꼭 넣어줘야한다.
문제

 

내 풀이, dfs
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

class Main {
    static int N;
    static char target;
    static int[] checkX = {0, 1, 0, -1};
    static int[] checkY = {1, 0, -1, 0};
    static char[][] rgb;
    static boolean[][] visited;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        N = Integer.parseInt(br.readLine());

        rgb = new char[N][];
        visited = new boolean[N][N];

        for (int i = 0; i < N; i++) {
            rgb[i] = br.readLine().toCharArray();
        }
        int eye1 = 0;
        for (int x = 0; x < N; x++) {
            for (int y = 0; y < N; y++) {
                target = rgb[x][y] ;
                if (target == 'R') {
                    rgb[x][y] = 'G';
                }
                if (!visited[x][y]) {
                    dfs(target, x, y);
                    eye1++;
                }
            }
        }
        visited = new boolean[N][N];
        int eye2 = 0;
        for (int x = 0; x < N; x++) {
            for (int y = 0; y < N; y++) {
                target = rgb[x][y] ;
                if (!visited[x][y]) {
                    dfs(target, x, y);
                    eye2++;
                }
            }
        }
        System.out.println(eye1 + " " + eye2);
    }
    public static void dfs(char target, int x, int y) {
        if (!visited[x][y]) {
            visited[x][y] = true;
        } else {
            return;
        }
        for (int i = 0; i < 4; i++) {
            if (x + checkX[i] >= 0 && x + checkX[i] < N
            && y + checkY[i] >= 0 && y + checkY[i] < N
            && rgb[x + checkX[i]][y + checkY[i]] == target) {
                dfs(target, x + checkX[i], y + checkY[i]);
            }
        }
    }
}

 

리뷰

2차원 배열 RGB가 표현 된다. 이를 하나의 그림으로 표현했을 때 각각 R, G, B의 색이 나뉘는 구역이 몇개인지 구분하여 일반적인 시선에서의 개수와 적록색약 시선에서의 개수를 출력해내는 문제이다.

 

예제 문제

RRRBB
GGBBB
BBBRR
BBRRR
RRRRR

 

위의 예제 문제를 그림으로 나타내보았다.

좌, 일반적인 시선 우, 적록색약의 시선

이와 같이 그림으로 표현해보면 구분하기가 쉬운데, 일반적인 시선으로 볼 땐 네개의 구역으로 나뉘고

적록색약의 시선은 R과 G의 분간이 어렵기 때문에 세개의 구역으로 나뉘는 걸 확인 할 수 있다.

 

dfs를 이용하여 풀어낸 문제이다.

rgb 배열과 같은 크기의 boolean 2 차원배열을 만든다.

rgb 배열의 0, 0  부터 방문하고 같은 위치의 boolean 배열의 값은 true로 바꿔준다. 현재 위치의 rgb 값중 하나(예시에선 

첫번째 R 값)를 target으로 지정해준다. 

순서대로 우, 하, 좌, 상 순서대로 돌며 해당 타겟과 같은 값을 가진 index는 boolean 배열에 방문 했다는 의미로 모두 true로 바꿔준다. 이렇게 한 사이클을 돌면 인접한 R 값을 가진 구역은 모두 true로 바뀌게 되며 그때 카운트를 1 (예시에선 eye1)올려준다.

 

이런식으로 모든 구역을 돌게 되는데 적록색약의 시선에서 카운터를 다시 해야하기 떄문에, 일반 시선 카운터를 돌 때 R 값을 G 값으로 바꿔주거나 G를 R로 바꿔주는 작업을 가미한다. 

 

이후 boolean 값을 초기하 한 뒤 다시 한번 배열을 돌며 적록색약의 카운트(예시에선 eye2)를 올려준다.

 

visited 배열을 활용해볼 수 있는 문제였다. 

문제

내 풀이
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int total = sc.nextInt();
        int items = sc.nextInt();
        int sum = 0;

        for (int i = 0; i < items; i++) {
            int a = sc.nextInt();
            int b = sc.nextInt();

            sum += a * b;
        }
        if (sum == total)
            System.out.println("Yes");
        else
            System.out.println("No");
    }
}

 

내 풀이 _ 예제 입력 맞춰보느냐고 ㅜㅜ
예제 입력

260000 4
20000 5
30000 2
10000 6
5000 8
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        Map<Integer, Integer> map = new HashMap();
        StringTokenizer st;

        int totalP = Integer.parseInt(br.readLine());
        int items = Integer.parseInt(br.readLine());

        for (int i = 0; i < items; i++) {
            st = new StringTokenizer(br.readLine());
            map.put(Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken()));
        }
        int sumP = 0;
        for (Map.Entry<Integer, Integer> cur : map.entrySet()) {
            sumP += cur.getKey() * cur.getValue();
        }
        System.out.println(totalP == sumP ? "Yes" : "No");
    }
}
리뷰
문제 자체를 크게 리뷰할만한 건 없었지만, 입력 예제의 형식을 맞춰보느라 StringTokenizser를 사용하는 과정에서 배운점이 있어 작성한다. 두번째 풀이 형식으로 작성하면 예제의 입력 형식도 맞출 수 있다. 그치만 정답은 아니라고 나온다. 

왜 인가 곰곰이 생각해보니 map을 사용해 Map.Entry로 key 값과 value 값을 불러와 사용했는데, 이 방식이 문제 였다. 물건의 가격을 key값으로 저장 했는데, 물건의 가격이 같은 물건이 있을 경우 key 값의 중복이 어려워 기존의 key의 value 값이 업데이트 돼 버리는 게 문제였다.

이 방식으로 이 문제를 해결 하려면 Map의 제네릭을 <Integer, Integer[]>와 같이 value 값을 배열 또는 리스트로 지정하여 같은 key 값이 존재해도 그에 해당하는 value를 배열 또는 리스트의 요소로 지정해 계산에 각자의 요소를 불러오게 하는 방법이 존재 한다.

그치만 그렇게 할 경우 시간복잡도도, 공간복잡도도 더 소요되게 된다. 그냥 첫번째의 방식으로 풀어내는 게 낫다.

그래도 Map.Entry를 for each로 사용해 key와 value 모두를 연산에 사용할 수 있는 방식을 연습할 수 있었다.

for (Map.Entry<Integer, Integer> cur : map.entrySet()) {
      sumP += cur.getKey() * cur.getValue();
}
문제

 

내 풀이
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 a = Integer.parseInt(br.readLine());
        int b = Integer.parseInt(br.readLine());

        int sum = 0;
        String[] B = Integer.toString(b).split("");
        for (int i = B.length - 1; i >= 0; i--) {
            B[i] = (a * Integer.parseInt(B[i]) + "");
            System.out.println(B[i]);
            for (int j = 0; j < Math.abs((B.length - 1) - i); j++) {
                B[i] += 0;
            }
            sum += Integer.parseInt(B[i]);
        }
        System.out.println(sum);
    }
}

 

다른 사람의 풀이
import java.io.*;

public class Main {

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

		int a = Integer.parseInt(br.readLine());
		int b = Integer.parseInt(br.readLine());

		System.out.println(a * (b % 10));
		System.out.println(a * ((b % 100) / 10));
		System.out.println(a * (b / 100));
		System.out.println(a * b);
	}
}
문제
주희는 아파트에 살고 있다. 이 아파트엔 특이한 입주 규칙이 있다.
내가 만약 2층의 3호에 살고 있다면 하나 아래층인 1층의 1호 ~ 3호까지 살고 있는 인원 만큼 함께 입주해야한다.
2층의 5호라면 1층의 1호 ~ 5호까지 살고 있는 인원 만큼 함께 입주 해야한다.

아파트는 0층부터 시작하며 0층은 각 호당 호 만큼의 인원이 살고 있다.(1호는 1명 2호는 2명...)
이 규칙은 모든 거주지에 적용돼 있으며 비어있는 거주지는 없다.

이때 주어지는 거주지에 살고 있는 인원을 출력하는 문제이다.

 

내 풀이, dp
import java.util.Scanner;

public class Main {
    public static int live(int k, int n) {
        int[][] dp = new int[k + 1][n];
        for (int i = 0; i < dp.length; i++) {
            for (int j = 0; j < dp[i].length; j++) {
                if (i == 0) dp[i][j] = j + 1;
                else if (j == 0) dp[i][j] = 1;
                else if (j == 1) dp[i][j] = i + 2;
                else dp[i][j] = dp[i][j - 1] + dp[i - 1][j];
            }
        }
        return dp[k][n - 1];
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int T = sc.nextInt();

        for (int i = 0; i < T; i++) {
            int k = sc.nextInt();
            int n = sc.nextInt();
            System.out.println(live(k, n));
        }
    }
}

 

리뷰

아파트에 살고 있는 인원을 시각화 해보았다.

 

0층은 각 호수 만큼의 인원이 살고 있으며, (i가 0인 경우)

if (i == 0) dp[i][j] = j + 1;

각 층의 1호는 무조건 1명이 살고 있고 (j가 0인 경우)

else if (j == 0) dp[i][j] = 1;

각 층의 2호는 현재층 + 2명이 살고 있는 것을 알 수 있다. (j가 1인 경우)

else if (j == 1) dp[i][j] = i + 2;

 

이외의 호수는 (내 호수 - 1호의 인원) + (내 층 - 1층의 내 호수 인원)으로 알 수 있다.

else dp[i][j] = dp[i][j - 1] + dp[i - 1][j];

 

이렇게 dp 테이블을 만들어 둔 후 return dp[k][n - 1]; 해주면 해당 층의 호수에 살고 있는 인원을 알 수 있다.

(아파트의 입주 규칙이 너무 이상하다..)

문제
상근이는 설탕 배달을 가야한다. 설탕은 3kg 패키지와 5kg 패키지로 구성 돼 있다.
배달 가야하는 설탕 n이 주어질 때 설탕 패키지를 가장 적게 가져갈 수 있는 수를 구하는 문제이다.
설탕의 무게는 주어진 무게로 맞아 떨어져야하며 그렇지 못하다면 -1을 출력한다.

 

내 풀이 1, 그리디 알고리즘
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int cnt = 0;

        while (true) {
            if (n % 5 == 0) {
                cnt += n / 5;
                break;
            } else {
                if (n < 3) {
                    cnt = -1;
                    break;
                }
                n -= 3;
                cnt++;
            }
        }
        System.out.println(cnt);
    }
}

 

내 풀이 2, dp
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();

        int[] dp = new int[n + 1];
        for (int i = 0; i <= n ; i++) {
            if (i != 0 && i % 5 == 0) {
                dp[i] = i / 5;
            } else if (i != 0 && i % 3 == 0) {
                dp[i] = i / 3;
            }
            if (i < 3 || i == 4 || i == 7) {
                dp[i] = -1;
                continue;
            }
            if (dp[i - 3] != -1) {
                dp[i] = Math.min(dp[i], dp[i - 3] + 1);
                if (dp[i] == 0) dp[i] = dp[i - 3] + 1;
            }
        }
        System.out.println(dp[n]);
    }
}
리뷰
최적해를 찾는 그리디 문제이다.
처음 조건을 정할 때 3과 5로 나누어 떨어지는 등 고민을 많이 했었다.
위의 풀이에선 5로 나누어 떨이지면 5로 나누어 끝내고, 그렇지 못하다면 5로 나누어 떨어질 때 까지 3을 뺀다. 뺄 때 마다 패키지수(위에선 cnt)를 올린 후 5로 나누어 끝냈다.

최적해를 찾는 문제는 조건을 항상 오랫동안 고민하게 된다. 수학적으로 푸는 풀이도 보았지만 실제 코딩테스트였다면 그리디 문제는 수학적 접근 보다 확실히 그리디 방식으로 접근하는 게 빠를 것 같다.

조건을 세우는 작업에 익숙해지자.


+ 내 풀이2는 dp를 활용해서 풀어봤다.
dp 배열에 -1 또는 자기 자신 -3 배열의 요소 + 1 해주면 답이 구해지나, -1의 위치가 예외적이었던 걸 조건으로 깔끔하게 풀어내진 못한 것 같다. 차차 dp 배열 만들기가 조금 더 깔끔해질 수 있게 해야겠다.

+ Recent posts