CT

[BOJ] 25단계 분할 정복

kinggora 2023. 5. 9. 00:44
1 2630 색종이 만들기
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

    private static int white = 0;
    private static int blue = 0;
    private static int[][] paper;


    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(reader.readLine());
        paper = new int[N][N];
        for(int i = 0; i < N; i++) {
            StringTokenizer tokenizer = new StringTokenizer(reader.readLine());
            for(int j = 0; j < N; j++) {
                paper[i][j] = Integer.parseInt(tokenizer.nextToken());
            }
        }
        recursion(0, 0, N);
        System.out.println(white);
        System.out.println(blue);
    }

    public static void recursion(int x, int y, int size) {
        int color = paper[x][y];
        boolean isSameColor = true;
        for(int i = x; i < x + size; i++) {
            for(int j = y; j < y + size; j++) {
                if(color != paper[i][j]) {
                    isSameColor = false;
                    break;
                }
            }
        }
        if(isSameColor) {
            if(color == 0) {
                white++;
            } else {
                blue++;
            }
        } else {
            int newSize = size / 2;
            recursion(x, y, newSize);
            recursion(x + newSize, y, newSize);
            recursion(x, y + newSize, newSize);
            recursion(x + newSize, y + newSize, newSize);
        }
    }

}

 

2 1992 쿼드트리
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
    private static char[][] image;

    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(reader.readLine());
        image = new char[N][N];
        for(int i = 0; i < N; i++) {
            String str = reader.readLine();
            for(int j = 0; j < N; j++) {
                image[i][j] = str.charAt(j);
            }
        }
        System.out.println(recursion(0, 0, N));
    }

    public static String recursion(int x, int y, int size) {
        char color = image[x][y];
        boolean checkColor = true;
        for(int i = x; i < x + size; i++) {
            for(int j = y; j < y + size; j++) {
                if(color != image[i][j]) {
                    checkColor = false;
                    break;
                }
            }
        }
        if(checkColor) {
            return String.valueOf(color);
        }
        int newSize = size / 2;
        StringBuilder sb = new StringBuilder();
        sb.append("(")
                .append(recursion(x, y, newSize))
                .append(recursion(x, y + newSize, newSize))
                .append(recursion(x + newSize, y, newSize))
                .append(recursion(x + newSize, y + newSize, newSize))
                .append(")");
        return sb.toString();
    }

}

*문자열을 병합(정복)하는 과정에서 순서 주의: 왼쪽 위 + 오른쪽 위 + 왼쪽 아래 + 오른쪽 아래

 

3 1780 종이의 개수
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
    private static int[][] paper;
    private static int[] count = new int[3];

    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(reader.readLine());
        paper = new int[N][N];
        for(int i = 0; i < N; i++) {
            StringTokenizer tokenizer = new StringTokenizer(reader.readLine());
            for(int j = 0; j < N; j++) {
                paper[i][j] = Integer.parseInt(tokenizer.nextToken()) + 1;
            }
        }
        recursion(0, 0, N);
        for(int i = 0; i < count.length; i++) {
            System.out.println(count[i]);
        }
    }

    public static void recursion(int x, int y, int size) {
        if(checkNumber(x, y, size)) {
            int idx = paper[x][y];
            count[idx]++;
        } else {
            int newSize = size / 3;
            for(int i = 0; i < 3; i++) {
                for(int j = 0; j < 3; j++) {
                    recursion(x + i * newSize, y + j * newSize, newSize);
                }
            }
        }
    }

    public static boolean checkNumber(int x, int y, int size) {
        if(size == 1) {
            return true;
        }
        int num = paper[x][y];
        for(int i = x; i < x + size; i++) {
            for(int j = y; j < y + size; j++) {
                if(num != paper[i][j]) {
                    return false;
                }
            }
        }
        return true;
    }
}

*배열을 통해 카운팅하기 위해 입력(-1, 0, 1)을 인덱스 범주(0, 1, 2)로 치환하여 저장함

 

4 1629 곱셈

A, B, C가 모두 2,147,483,647 이하의 자연수일 때 A^B % C 구하기

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

public class Main {

    private static int C;

    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokenizer = new StringTokenizer(reader.readLine());
        int A = Integer.parseInt(tokenizer.nextToken());
        int B = Integer.parseInt(tokenizer.nextToken());
        C = Integer.parseInt(tokenizer.nextToken());

        System.out.println(pow(A, B));
    }

    public static long pow(long num, long n) {
        if(n == 1) {
            return num % C;
        }
        long temp = pow(num, n / 2);
        if(n % 2 == 0) {
            return temp * temp % C;
        } else {
            return (temp * temp % C * num % C) % C;
        }
    }
}

1. 분할 정복을 이용한 거듭제곱 최적화: C^n

  • n이 짝수일 때: C^n/2 * C^n/2
  • n이 홀수일 때: C^(n-1)/2 * C^(n-1)/2 * C  => (n-1)/2 + (n-1)/2 + 1 = n

2. 모듈러 산술(Modular Arithmetic)의 분배 법칙

  • (a + b) mod n = (a mod n + b mod n) mod n
  • (a - b) mod n = (a mod n - b mod n) mod n
  • (a * b) mod n = (a mod n * b mod n) mod n

 

5 11401 이항 계수 3

분할 정복을 사용한 거듭제곱과 페르마의 소정리를 이용해 곱셈의 역원을 구하기

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

public class Main {

    private static final int MOD = 1000000007;

    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String[] split = reader.readLine().split(" ");
        int N = Integer.parseInt(split[0]);
        int K = Integer.parseInt(split[1]);
        //nCk = n! / k!(n-k)!
        long numer = factorial(N); // N! % MOD
        long denom = factorial(K) * factorial(N-K) % MOD; // (K! % MOD * (N-K)! % MOD) % MOD = K!(N-K)! % MOD
        long result = numer * pow(denom, MOD - 2) % MOD; // 페르마의 소정리
        System.out.println(result);
    }

    //N! % MOD 를 구하는 메서드
    public static long factorial(int n) {
        long result = 1L;
        for(int i = 2; i <= n; i++) {
            result = result * i % MOD;
        }
        return result;
    }

    //C^n % MOD 를 구하는 메서드
    public static long pow(long C, int n) {
        if(n == 1) {
            return C % MOD;
        }
        long temp = pow(C, n / 2);
        if(n % 2 == 0) {
            return temp * temp % MOD;
        } else {
            return (temp * temp % MOD) * C % MOD;
        }
    }
}

 

6 2740 행렬 곱셈
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

    private static final int MOD = 1000000007;

    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String[] split = reader.readLine().split(" ");
        int N = Integer.parseInt(split[0]);
        int M = Integer.parseInt(split[1]);
        int[][] A = new int[N][M];
        for(int i = 0; i < N; i++) {
            StringTokenizer tokenizer = new StringTokenizer(reader.readLine());
            for(int j = 0; j < M; j++) {
                A[i][j] = Integer.parseInt(tokenizer.nextToken());
            }
        }
        split = reader.readLine().split(" ");
        int K = Integer.parseInt(split[1]);
        int[][] result = new int[N][K];
        for(int i = 0; i < M; i++) {
            StringTokenizer tokenizer = new StringTokenizer(reader.readLine());
            for(int j = 0; j < K; j++) {
                int n = Integer.parseInt(tokenizer.nextToken());
                for(int k = 0; k < N; k++) {
                    result[k][j] += A[k][i] * n;
                }
            }
        }
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < N; i++) {
            for(int j = 0; j < K; j++) {
                sb.append(result[i][j]).append(" ");
            }
            sb.append("\n");
        }
        System.out.print(sb);
    }
}

 

7 10830 행렬 제곱
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;


public class Main {

    private static final int MOD = 1000;
    private static int N;

    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokenizer = new StringTokenizer(reader.readLine());
        N = Integer.parseInt(tokenizer.nextToken());
        long B = Long.parseLong(tokenizer.nextToken());
        int[][] matrix = new int[N][N];
        for(int i = 0; i < N; i++) {
            tokenizer = new StringTokenizer(reader.readLine());
            for(int j = 0; j < N; j++) {
                matrix[i][j] = Integer.parseInt(tokenizer.nextToken());
            }
        }
        int[][] result = pow(matrix, B);
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < N; i++) {
            for(int j = 0; j < N; j++) {
                sb.append(result[i][j] % MOD).append(" ");
            }
            sb.append("\n");
        }
        System.out.print(sb);
    }

    public static int[][] pow(int[][] matrix, long n) {
        if(n == 1) {
            return matrix;
        }
        int[][] temp = pow(matrix, n / 2);
        if(n % 2 == 0) {
            return multiply(temp, temp);
        } else {
            return multiply(multiply(temp, temp), matrix);
        }
    }

    public static int[][] multiply(int[][] A, int[][] B) {
        int[][] result = 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++) {
                    result[i][j] += A[i][k] * B[k][j] % MOD;
                }
            }
        }
        return result;
    }
}

 

8 11444 피보나치 수 6

행렬({{1,1},{1,0}})의 거듭제곱을 통해 {{Fn+1 Fn}, {Fn, Fn-1}}을 계산

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


public class Main {

    private static final int MOD = 1000000007;

    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        long N = Long.parseLong(reader.readLine());
        long[][] matrix = {{1, 1}, {1, 0}};
        System.out.print(pow(matrix, N - 1)[0][0]);
    }

    public static long[][] pow(long[][] matrix, long n) {
        if(n == 0 || n == 1) {
            return matrix;
        }
        long[][] temp = pow(matrix, n / 2);
        if(n % 2 == 0) {
            return multiply(temp, temp);
        } else {
            return multiply(multiply(temp, temp), matrix);
        }
    }

    public static long[][] multiply(long[][] A, long[][] B) {
        long[][] result = new long[2][2];
        for(int i = 0; i < 2; i++) {
            for(int j = 0; j < 2; j++) {
                for(int k = 0; k < 2; k++) {
                    result[i][j] += A[i][k] * B[k][j];
                }
                result[i][j] %= MOD;
            }
        }
        return result;
    }
}

*N이 1,000,000,000,000,000,000 까지므로 재귀나 배열을 통한 동적계획법을 사용할 수 없다.

*N-1 제곱을 통해 Fn을 계산