www.acmicpc.net/problem/1931

 

1931번: 회의실 배정

(1,4), (5,7), (8,11), (12,14) 를 이용할 수 있다.

www.acmicpc.net

문제

한 개의 회의실이 있는데 이를 사용하고자 하는 N개의 회의에 대하여 회의실 사용표를 만들려고 한다. 각 회의 I에 대해 시작시간과 끝나는 시간이 주어져 있고, 각 회의가 겹치지 않게 하면서 회의실을 사용할 수 있는 회의의 최대 개수를 찾아보자. 단, 회의는 한번 시작하면 중간에 중단될 수 없으며 한 회의가 끝나는 것과 동시에 다음 회의가 시작될 수 있다. 회의의 시작시간과 끝나는 시간이 같을 수도 있다. 이 경우에는 시작하자마자 끝나는 것으로 생각하면 된다.

입력

첫째 줄에 회의의 수 N(1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N+1 줄까지 각 회의의 정보가 주어지는데 이것은 공백을 사이에 두고 회의의 시작시간과 끝나는 시간이 주어진다. 시작 시간과 끝나는 시간은 231-1보다 작거나 같은 자연수 또는 0이다.

출력

첫째 줄에 최대 사용할 수 있는 회의의 최대 개수를 출력한다.

예제 입력 1

11

1 4

3 5

0 6

5 7

3 8

5 9

6 10

8 11

8 12

2 13

12 14

예제 출력 1

4

 

 

 

 

 

 

풀이 .

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

class Meeting implements Comparable<Meeting>{
    int begin;
    int end;
    public Meeting(int begin, int end) {
        this.begin = begin;
        this.end = end;
    }
    @Override
    public int compareTo(Meeting o) {
        if(this.end != o.end) {
            if(this.end < o.end) {
                return -1;
            }else {
                return 1;
            }
        }else {
            if(this.begin < o.begin) {
                return -1;
            }else {
                return 1;
            }
        }
    }
}

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());

        ArrayList<Meeting> list = new ArrayList<>();
        for(int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine());
            int begin = Integer.parseInt(st.nextToken());
            int end = Integer.parseInt(st.nextToken());
            list.add(new Meeting(begin, end));
        }

        Collections.sort(list);
        int ans = 0;
        int time = 0;
        for(Meeting m : list) {
            if(m.begin >= time) {
                ans += 1;
                time = m.end;
            }
        }
        System.out.println(ans);
    }
}

 

그리디 문제.

 

회의가 끝나는 순서를 기준으로 정렬하여 처리하면 최대 회의 개수가 나온다.

 

 

 

'알고리즘 문제 > 백준 온라인 저지' 카테고리의 다른 글

[BOJ] 1744 - 수 묶기 JAVA  (0) 2021.02.10
[BOJ] 11399 - ATM JAVA  (0) 2021.02.10
[BOJ] 1783 - 병든 나이트 JAVA  (0) 2021.02.09
[BOJ] 10610 - 30 JAVA  (0) 2021.02.09
[BOJ] 2875 - 대회 or 인턴 JAVA  (0) 2021.01.26

www.acmicpc.net/problem/1783

 

1783번: 병든 나이트

첫째 줄에 체스판의 세로 길이 N와 가로 길이 M이 주어진다. N과 M은 2,000,000,000보다 작거나 같은 자연수이다.

www.acmicpc.net

문제

병든 나이트가 N × M 크기 체스판의 가장 왼쪽아래 칸에 위치해 있다. 병든 나이트는 건강한 보통 체스의 나이트와 다르게 4가지로만 움직일 수 있다.

  1. 2칸 위로, 1칸 오른쪽
  2. 1칸 위로, 2칸 오른쪽
  3. 1칸 아래로, 2칸 오른쪽
  4. 2칸 아래로, 1칸 오른쪽

병든 나이트는 여행을 시작하려고 하고, 여행을 하면서 방문한 칸의 수를 최대로 하려고 한다. 병든 나이트의 이동 횟수가 4번보다 적지 않다면, 이동 방법을 모두 한 번씩 사용해야 한다. 이동 횟수가 4번보다 적은 경우(방문한 칸이 5개 미만)에는 이동 방법에 대한 제약이 없다.

체스판의 크기가 주어졌을 때, 병든 나이트가 여행에서 방문할 수 있는 칸의 최대 개수를 구해보자.

입력

첫째 줄에 체스판의 세로 길이 N와 가로 길이 M이 주어진다. N과 M은 2,000,000,000보다 작거나 같은 자연수이다.

출력

병든 나이트가 여행에서 방문할 수 있는 칸의 개수중 최댓값을 출력한다.

예제 입력 1

100 50

예제 출력 1

48

예제 입력 2

1 1

예제 출력 2

1

예제 입력 3

17 5

예제 출력 3

4

예제 입력 4

2 4

예제 출력 4

2

예제 입력 5

20 4

예제 출력 5

4

 

 

 

 

 

 

 

풀이 .

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 st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());  // height
        int m = Integer.parseInt(st.nextToken());  // width

        int ans = 0;
        if(n == 1) ans = 1;
        if(n == 2) ans = Math.min(4, (m+1) / 2);
        if(n >= 3) {
            if(m >= 7) {
                ans = m - 2;
            }else {
                ans = Math.min(4, m);
            }
        }
        System.out.println(ans);
    }
}

 

문제의 조건 설명이 불친절해서 성질만 잔뜩 돋군 문제.

 

결국 요구사항을 제대로 이해하지도 못하고 정답을 확인했다.

 

문제의 요구조건을 자세히 풀어쓰면 아래와 같다.

 

 

나이트가 매 순간 이동할 위치를 선택하여 이동할 때 방문할 수 있는 칸의 최대값을 구해야 한다.

(h, w)일 때 (-2, 1), (-1, 2), (1, 2), (2, 1) 이렇게 네 가지 경우의 이동 가능.

 

 

 

(제한 조건)

1. 우선 나이트의 시작위치가 없다. 나이트의 시작 위치는 NxM 사이즈에서 임의적으로 선택할 수 있다.

 

2. 나이트의 이동 횟수가 4번보다 적지 않다면, 이동 방법을 모두 한 번씩 사용해야 한다?

-> 4개 이상의 칸을 방문하기 위해서는 나이트의 네 가지 이동 방법을 모두 사용해야만 한다. 

-> 즉, 3개 이하의 방법만 사용하면서 4번 이상 이동할 수 없다. (= 시작점 포함 5칸 이상 방문할 수 없다)

 

3. 이동 횟수가 4번보다 적은 경우에는 이동 방법에 대한 제약이 없다.

-> 3번 까지만 이동한다면(= 시작점 포함하여 4칸만 방문한다면) 네 가지 이동 방법 중 어떤 방법을 이용하거나 이용하지 않는 것은 자유롭게 선택 가능하다.

 

 

 

(경우의 수)

주어진 경우에서 경우의 수를 셋으로 나누어 생각할 수 있다.

 

1. 높이가 1인 경우

-> 나이트의 네 가지 이동 방법 모두 높이를 최소 1칸은 변경해야 한다.

-> 즉, 체스판의 높이가 1이 전부라면 아무 칸으로도 이동할 수 없다는 뜻이다.

-> 단, 시작 위치는 자유롭게 정할 수 있기 때문에 이 경우 답은 1이 된다.

 

2. 높이가 2인 경우

-> (-1. 2), (1, 2) 두 가지 이동 방법만 사용할 수 있다.

-> 이를 식으로 나타내면 (width + 1) / 2 이다.

-> 단, 위의 제한조건 2에 의해 네 가지 방법을 다 사용하지 않는다면 최대 이동횟수는 3번이 전부이다. (= 시작점 포함 4칸)

-> 따라서 Math.min(4, (width + 1) / 2)가 답이 된다.

 

3. 높이가 3 이상인 경우

-> 이 경우는 다시 넓이가 7 이상인지 아닌지로 나뉘게 된다. (= 네 가지 이동 방법을 모두 사용할 수 있는지 아닌지)

 

3.1 넓이가 7 이상인 경우 (= 네 가지 이동 방법 모두 사용 가능한 경우)

-> 높이가 3 이상이기 때문에 (-2, 1), (2, 1) 이동도 사용이 가능하다.

-> 일단 네 가지 이동 방법을 한 번씩 사용하면 오른쪽으로 6칸 움직이게 된다. (현 가로 위치 7)

-> 여기에서 (-1, 2), (1, 2) 두 이동에 대해서 방문 칸이 없는 컬럼이 하나씩 발생한다. (현재 방문한 칸이 존재하는 컬럼 5개)

-> 그 이후로는 최대한 많은 칸을 방문하는 것이 목적이기 때문에 열을 한 칸 씩만 이동하는 (-2, 1), (2, 1)만 번갈아가며 사용한다. 즉, 열 1칸 당 방문 칸 1개가 추가된다.

-> 따라서 총 열의 개수 -2를 해주면 답이 된다.

 

3.2 넓이가 7 미만인 경우 (= 네 가지 이동 방법을 모두 사용하지는 못 하는 경우)

-> 높이가 3 이상이기 때문에 (-2, 1), (2, 1) 이동도 사용이 가능하지만 그래봤자 네 가지 이동 방법을 전부 사용할 수는 없다. 즉, 답은 4를 넘길 수 없다.

-> 어차피 4를 못 넘길 거 굳이 열을 많이 잡아먹는 (-1, 2), (1, 2)를 사용할 필요가 없다.

-> (-2, 1), (2, 1)만 번갈아가며 사용하며 열마다 한 개의 방문 칸을 만들어 준다.

-> 단, 제한 조건 2에 의해 Math.min(4, width)가 정답이 된다,

 

 

 

 

 

 

 

 

 

'알고리즘 문제 > 백준 온라인 저지' 카테고리의 다른 글

[BOJ] 11399 - ATM JAVA  (0) 2021.02.10
[BOJ] 1931 - 회의실 배정 JAVA  (0) 2021.02.10
[BOJ] 10610 - 30 JAVA  (0) 2021.02.09
[BOJ] 2875 - 대회 or 인턴 JAVA  (0) 2021.01.26
[BOJ] 11047 - 동전 0 JAVA  (0) 2021.01.26

www.acmicpc.net/problem/10610

 

10610번: 30

어느 날, 미르코는 우연히 길거리에서 양수 N을 보았다. 미르코는 30이란 수를 존경하기 때문에, 그는 길거리에서 찾은 수에 포함된 숫자들을 섞어 30의 배수가 되는 가장 큰 수를 만들고 싶어한

www.acmicpc.net

문제

어느 날, 미르코는 우연히 길거리에서 양수 N을 보았다. 미르코는 30이란 수를 존경하기 때문에, 그는 길거리에서 찾은 수에 포함된 숫자들을 섞어 30의 배수가 되는 가장 큰 수를 만들고 싶어한다.

미르코를 도와 그가 만들고 싶어하는 수를 계산하는 프로그램을 작성하라.

입력

N을 입력받는다. N는 최대 105개의 숫자로 구성되어 있으며, 0으로 시작하지 않는다.

출력

미르코가 만들고 싶어하는 수가 존재한다면 그 수를 출력하라. 그 수가 존재하지 않는다면, -1을 출력하라.

예제 입력 1

30

예제 출력 1

30

예제 입력 2

102

예제 출력 2

210

예제 입력 3

2931

예제 출력 3

-1

예제 입력 4

80875542

예제 출력 4

88755420

 

 

 

 

 

 

 

 

풀이 .

import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringBuilder sb = new StringBuilder();
        String n = br.readLine();
        int[] cnt = new int[10];

        long total = 0;
        for(int i = 0; i < n.length(); i++) {
            int temp = n.charAt(i) - '0';
            cnt[temp] += 1;
            total += temp;
        }

        // 30의 배수는 10의 배수이면서 3의 배수이다
        // 10의 배수는 반드시 0을 하나 이상 가져야 한다
        // 3의 배수이려면 모든 자리수의 합이 3의 배수여야 한다
        if(!n.contains("0") || total % 3 != 0) {
            bw.write("-1");
            br.close();
            bw.flush();
            bw.close();
            return;
        }

        // 큰 수부터 그 개수를 찍어준다
        for(int i = 9; i >= 0; i--) {
            for(int j = 0; j < cnt[i]; j++) {
                sb.append(String.valueOf(i));
            }
        }

        bw.write(sb.toString());
        br.close();
        bw.flush();
        bw.close();
    }
}

 

처음 봤을 때는 모든 조합을 구해서 체크하는 것인가 했지만 절대 그럴 수 없다.

 

입력받는 문자의 길이가 10^5이기 때문에 무조건 시간초과이다.

 

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

 

30의 배수는 10의 배수이면서 동시에 3의 배수이다.

 

1. 반드시 0을 포함 해야한다. (10의 배수 조건)

2. 모든 자리의 수를 더하면 3의 배수가 되어야 한다. (3의 배수 조건)

 

30의 배수가 될 수 없는 조건을 체크하여 예외처리를 먼저 한다.

 

예외처리를 통과하고 나면 어차피 전부 3의 배수이므로 가장 큰 순서부터 보유한 개수만큼 하나씩 출력하면 된다.

 

 

 

 

 

www.acmicpc.net/problem/2875

 

2875번: 대회 or 인턴

첫째 줄에 N, M, K가 순서대로 주어진다. (0 ≤ M ≤ 100, 0 ≤ N ≤ 100, 0 ≤ K ≤ M+N),

www.acmicpc.net

문제

백준대학교에서는 대회에 나갈 때 2명의 여학생과 1명의 남학생이 팀을 결성해서 나가는 것이 원칙이다. (왜인지는 총장님께 여쭈어보는 것이 좋겠다.)

백준대학교는 뛰어난 인재들이 많아 올해에도 N명의 여학생과 M명의 남학생이 팀원을 찾고 있다. 대회에 참여하려는 학생들 중 K명은 반드시 인턴쉽 프로그램에 참여해야 한다. 인턴쉽에 참여하는 학생은 대회에 참여하지 못한다.

백준대학교에서는 뛰어난 인재들이 많기 때문에, 많은 팀을 만드는 것이 최선이다.

여러분은 여학생의 수 N, 남학생의 수 M, 인턴쉽에 참여해야하는 인원 K가 주어질 때 만들 수 있는 최대의 팀 수를 구하면 된다.

입력

첫째 줄에 N, M, K가 순서대로 주어진다. (0 ≤ M ≤ 100, 0 ≤ N ≤ 100, 0 ≤ K ≤ M+N),

출력

만들 수 있는 팀의 최대 개수을 출력하면 된다.

예제 입력 1

6 3 2

예제 출력 1

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 st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());  // 여
        int m = Integer.parseInt(st.nextToken());  // 남
        int k = Integer.parseInt(st.nextToken());  // 인턴
        int total = n + m;

        int team = 0;
        while(total - 3 >= k) {
            if(n >= 2 && m >= 1) {
                n -= 2;
                m -= 1;
                total -= 3;
                team += 1;
            }else {
                break;
            }
        }
        System.out.println(team);
    }
}

 

 

 

'알고리즘 문제 > 백준 온라인 저지' 카테고리의 다른 글

[BOJ] 1783 - 병든 나이트 JAVA  (0) 2021.02.09
[BOJ] 10610 - 30 JAVA  (0) 2021.02.09
[BOJ] 11047 - 동전 0 JAVA  (0) 2021.01.26
[BOJ] 2448 - 별 찍기 - 11 JAVA  (0) 2021.01.25
[BOJ] 2447 - 별 찍기 - 10 JAVA  (0) 2021.01.23

www.acmicpc.net/problem/11047

 

11047번: 동전 0

첫째 줄에 N과 K가 주어진다. (1 ≤ N ≤ 10, 1 ≤ K ≤ 100,000,000) 둘째 줄부터 N개의 줄에 동전의 가치 Ai가 오름차순으로 주어진다. (1 ≤ Ai ≤ 1,000,000, A1 = 1, i ≥ 2인 경우에 Ai는 Ai-1의 배수)

www.acmicpc.net

문제

준규가 가지고 있는 동전은 총 N종류이고, 각각의 동전을 매우 많이 가지고 있다.

동전을 적절히 사용해서 그 가치의 합을 K로 만들려고 한다. 이때 필요한 동전 개수의 최솟값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N과 K가 주어진다. (1 ≤ N ≤ 10, 1 ≤ K ≤ 100,000,000)

둘째 줄부터 N개의 줄에 동전의 가치 Ai가 오름차순으로 주어진다. (1 ≤ Ai ≤ 1,000,000, A1 = 1, i ≥ 2인 경우에 Ai는 Ai-1의 배수)

출력

첫째 줄에 K원을 만드는데 필요한 동전 개수의 최솟값을 출력한다.

예제 입력 1

10 4200

1

5

10

50

100

500

1000

5000

10000

50000

예제 출력 1

6

 

 

 

 

 

 

 

풀이 .

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 st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int k = Integer.parseInt(st.nextToken());
        int[] coins = new int[n];
        for(int i = 0; i < n; i++) {
            coins[i] = Integer.parseInt(br.readLine());
        }

        int ans = 0;
        for(int i = n-1; i >= 0; i--) {
            int queotient = k / coins[i];
            k -= coins[i] * queotient;
            ans += queotient;
        }
        System.out.println(ans);
    }
}

 

큰 동전부터 거꾸로 내려가며 사용한다.

www.acmicpc.net/problem/2448

 

2448번: 별 찍기 - 11

첫째 줄에 N이 주어진다. N은 항상 3×2k 수이다. (3, 6, 12, 24, 48, ...) (0 ≤ k ≤ 10, k는 정수)

www.acmicpc.net

문제

예제를 보고 규칙을 유추한 뒤에 별을 찍어 보세요.

입력

첫째 줄에 N이 주어진다. N은 항상 3×2k 수이다. (3, 6, 12, 24, 48, ...) (0 ≤ k ≤ 10, k는 정수)

출력

첫째 줄부터 N번째 줄까지 별을 출력한다.

예제 입력 1

24

예제 출력 1

백준에서 직접 확인

 

 

 

 

 

 

 

풀이 .

import java.io.*;

public class Main {
    static BufferedReader br = null;
    static BufferedWriter bw = null;
    static StringBuilder sb = null;
    static String[] arr = null;
    static int n;

    public static void divideAndConquer(int k) {
        int end = 3 * (int)Math.pow(2, k);  // k가 커질 때마다 라인이 두배씩 늘어난다
        int mid = end / 2;

        for(int i = mid; i < end; i++) {  // 아래쪽 라인 절반은 위쪽 라인 절반을 공백을 사이에 두고 두 번 이어붙인 것이다
            arr[i] = arr[i - mid] + " " + arr[i - mid];
        }

        sb.delete(0, sb.length());
        while(sb.length() < mid) {
            sb.append(" ");
        }

        for(int i = 0; i < mid; i++) {  // 위쪽 절반 라인은 양옆에 전체 라인 수의 절반만큼 공백이 추가된다
            arr[i] = sb.toString() + arr[i] + sb.toString();
        }
    }

    public static void main(String[] args) throws IOException {
        br = new BufferedReader(new InputStreamReader(System.in));
        bw = new BufferedWriter(new OutputStreamWriter(System.out));
        sb = new StringBuilder();

        n = Integer.parseInt(br.readLine());
        arr = new String[n];

        arr[0] = "  *  ";
        arr[1] = " * * ";
        arr[2] = "*****";

        for(int k = 1; 3 * (int)Math.pow(2, k) <= n; k++) {
            divideAndConquer(k);
        }

        sb.delete(0, sb.length());
        for(int i = 0; i < n; i++) {
            sb.append(arr[i]).append("\n");
        }

        bw.write(sb.toString());
        br.close();
        bw.flush();
        bw.close();
    }
}

 

예제 출력을 잘 살펴보면,

 

1. 처음 3줄이 규칙성을 가지고 반복된다. 

2. k가 1씩 커질 때마다 전체 라인 수는 2배로 늘어난다.

라는 사실을 알 수 있다.

 

총 n개의 줄을 [0]~[n-1] 에 채워야 한다고 할 때,

 

1. 위쪽 절반(0 ~ (n-1)/2) 은 이전 단계에서 양 옆에 공백을 전체 라인의 절반((n-1)/2)만큼 달아주면 된다.

2. 아래쪽 절반은 위쪽절반이 공백 하나를 사이에 두고 양 옆에 대칭으로 위치한 모양이 되어야 한다.

이는 [i] = [i - 총 라인 수의 절반]  + " " + [i - 총 라인 수의 절반] 으로 나타낼 수 있다.

 

 

 

www.acmicpc.net/problem/2447

 

2447번: 별 찍기 - 10

재귀적인 패턴으로 별을 찍어 보자. N이 3의 거듭제곱(3, 9, 27, ...)이라고 할 때, 크기 N의 패턴은 N×N 정사각형 모양이다. 크기 3의 패턴은 가운데에 공백이 있고, 가운데를 제외한 모든 칸에 별이

www.acmicpc.net

문제

재귀적인 패턴으로 별을 찍어 보자. N이 3의 거듭제곱(3, 9, 27, ...)이라고 할 때, 크기 N의 패턴은 N×N 정사각형 모양이다.

크기 3의 패턴은 가운데에 공백이 있고, 가운데를 제외한 모든 칸에 별이 하나씩 있는 패턴이다.

*** * * ***

N이 3보다 클 경우, 크기 N의 패턴은 공백으로 채워진 가운데의 (N/3)×(N/3) 정사각형을 크기 N/3의 패턴으로 둘러싼 형태이다. 예를 들어 크기 27의 패턴은 예제 출력 1과 같다.

입력

첫째 줄에 N이 주어진다. N은 3의 거듭제곱이다. 즉 어떤 정수 k에 대해 N=3k이며, 이때 1 ≤ k < 8이다.

출력

첫째 줄부터 N번째 줄까지 별을 출력한다.

예제 입력 1

27

예제 출력 1

제대로 찍을 수가 없다.

BOJ에서 직접 확인.

 

 

 

 

 

 

 

풀이 .

import java.io.*;

public class Main {
    static BufferedReader br = null;
    static BufferedWriter bw = null;
    static StringBuilder sb = null;
    static char[][] arr = null;

    public static void putSpace(int row, int col, int len) {
        for(int i = row; i < row + len; i++ ) {
            for(int j = col; j < col + len; j++) {
                arr[i][j] = ' ';
            }
        }
    }

    public static void divideAndConquer(int row, int col, int len) {  // row, col은 시작행, 시작열
        if(len == 1) {
            arr[row][col] = '*';
            return;
        }

        int nextLen = len / 3;
        for(int i = 0; i < 3; i++) {
            for(int j = 0; j < 3; j++) {
                if(i == 1 && j == 1) {  // 큰 덩어리의 [2][2]은 공백 출력
                    putSpace(row + i*nextLen, col + j*nextLen, nextLen);
                }else {
                    divideAndConquer(row + i*nextLen, col + j*nextLen, nextLen);
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        br = new BufferedReader(new InputStreamReader(System.in));
        bw = new BufferedWriter(new OutputStreamWriter(System.out));
        sb = new StringBuilder();

        int n = Integer.parseInt(br.readLine());
        arr = new char[n+1][n+1];
        divideAndConquer(1, 1, n);

        for(int i = 1 ; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                sb.append(arr[i][j]);
            }
            sb.append("\n");
        }

        bw.write(sb.toString());
        br.close();
        bw.flush();
        bw.close();
    }
}

 

예전에 이 문제를 처음 봤을 때 어마어마한 출력문을 보고 쫄아서 바로 닫아버렸던 기억이 있다.

 

재귀를 어떻게 풀어나가야 할 지 떠올리는 것은 어렵지 않았지만 구현은 좀 까다로웠던 것 같다.

엄청나게 어려운 문제는 아니지만 다른 문제보다도 이 문제를 풀고 나니 뭔가 실력이 조금은 향상됐다는 느낌이 들기도 한다.

 

 

처음에 가장 고민했던 부분은 출력의 줄바꿈을 어떻게 해줘야 할 지 였다.

재귀 조건은 간단했지만 재귀 내부에서 공백을 넣으려고 하니 정상적인 출력을 뽑아낼 수 없기 때문이다.

 

알고보니 이차원 배열로 간단하게 해결할 수 있었다. N x N 사이즈 이차원 배열의 각 좌표에 '*' or ' ' 을 넣고, 모든 재귀가 끝난 후에 배열 원소들을 출력해주면 된다.

 

고정관념에 사로잡혀서 모든 처리를 재귀 안에서 해주려고 하다보니 배열을 사용한 간단한 해결책을 생각하지 못했다.

 

큰 덩어리를 9개로 나눈다는 것이 codeung.tistory.com/194 와 비슷하다.

N x N 크기의 덩어리를 아홉개로 나눴을때 한 덩어리에 N/3 크기인 덩어리들이 [3][3]으로 있다고 생각할 수 있다.

 

문제의 조건에 따라 가운데 덩어리인 [2][2]은 공백으로 채워줘야 한다.

(0행 0열은 사용하지 않는다 하고)

 

Length가 1이 될 때까지 재귀를 반복하고 1일 되면 별을 넣어준 후 return하면 된다.

www.acmicpc.net/problem/11729

 

11729번: 하노이 탑 이동 순서

세 개의 장대가 있고 첫 번째 장대에는 반경이 서로 다른 n개의 원판이 쌓여 있다. 각 원판은 반경이 큰 순서대로 쌓여있다. 이제 수도승들이 다음 규칙에 따라 첫 번째 장대에서 세 번째 장대로

www.acmicpc.net

문제

세 개의 장대가 있고 첫 번째 장대에는 반경이 서로 다른 n개의 원판이 쌓여 있다. 각 원판은 반경이 큰 순서대로 쌓여있다. 이제 수도승들이 다음 규칙에 따라 첫 번째 장대에서 세 번째 장대로 옮기려 한다.

  1. 한 번에 한 개의 원판만을 다른 탑으로 옮길 수 있다.
  2. 쌓아 놓은 원판은 항상 위의 것이 아래의 것보다 작아야 한다.

이 작업을 수행하는데 필요한 이동 순서를 출력하는 프로그램을 작성하라. 단, 이동 횟수는 최소가 되어야 한다.

아래 그림은 원판이 5개인 경우의 예시이다.

입력

첫째 줄에 첫 번째 장대에 쌓인 원판의 개수 N (1 ≤ N ≤ 20)이 주어진다.

 

출력

첫째 줄에 옮긴 횟수 K를 출력한다.

두 번째 줄부터 수행 과정을 출력한다. 두 번째 줄부터 K개의 줄에 걸쳐 두 정수 A B를 빈칸을 사이에 두고 출력하는데, 이는 A번째 탑의 가장 위에 있는 원판을 B번째 탑의 가장 위로 옮긴다는 뜻이다.

예제 입력 1

3

예제 출력 1

7

1 3

1 2

3 2

1 3

2 1

2 3

1 3

 

 

 

 

 

 

 

풀이 .

import java.io.*;

public class Main {
    static BufferedReader br = null;
    static BufferedWriter bw = null;
    static StringBuilder sb = null;
    static int cnt;

    public static void hanoi(int n, int from, int to, int temp) {
        if(n == 1) {
            sb.append(from + " " + to + "\n");
            cnt += 1;
        }else {
            hanoi(n-1, from, temp, to);
            sb.append(from + " " + to + "\n");
            cnt += 1;
            hanoi(n-1, temp, to, from);
        }
    }

    public static void main(String[] args) throws IOException {
        br = new BufferedReader(new InputStreamReader(System.in));
        bw = new BufferedWriter(new OutputStreamWriter(System.out));
        sb = new StringBuilder();

        int n = Integer.parseInt(br.readLine());
        hanoi(n, 1, 3, 2);

        System.out.println(cnt);
        bw.write(sb.toString());
        br.close();
        bw.flush();
        bw.close();
    }
}

 

재귀에 대한 통찰을 키울 수 있었던 문제.

 

 

N개의 원판을 장대1에서 장대3으로 옮기는 방법

1. N-1개의 원판을 2로 옮긴다.

2. N개중 가장 큰(맨 밑에 있던) 원판을 3으로 옮긴다.

3. 2에 있던 N-1개의 원판을 3으로 옮긴다.

(N-1개를 옮기는 과정에서는 N-2가, N-2개를 옮기는 과정에서는 N-3이 ... 이런식으로 재귀가 발생한다)

 

위 과정을 수행할 때 3개의 장대는 필연적으로 모두 사용되어진다.

이 셋을 각각 from, to, temp (출발지, 도착지, 경유지)로 구분하였다.

 

당연한 말이지만 3개의 장대 중에서 from, to를 제외한 나머지 하나가 temp가 된다.

 

처음에는 temp가 굳이 필요할까 라는 생각을 했지만 반드시 필요하다.

재귀가 이뤄지는 과정에서 from, to가 temp가 되어 들어가기도 하고 temp가 from, to가 되어 들어가기도 하기 때문에 반드시 temp가 필요하다.

 

하노이 탑의 시간 복잡도는 O(2^N)이다.

2^N - 1번의 연산으로 N개의 원판을 모두 옮길 수 있다.

 

 

 

아래 블로그의 글에서 큰 도움을 얻었다.

shoark7.github.io/programming/algorithm/tower-of-hanoi

+ Recent posts