더 많이 실패하기

공부 179-180일차: 백준 14391번 종이 조각 자바 java 본문

알고리즘/백준

공부 179-180일차: 백준 14391번 종이 조각 자바 java

김발자~ 2023. 1. 25. 18:15
반응형

14391 종이 조각

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

 

14391번: 종이 조각

영선이는 숫자가 쓰여 있는 직사각형 종이를 가지고 있다. 종이는 1×1 크기의 정사각형 칸으로 나누어져 있고, 숫자는 각 칸에 하나씩 쓰여 있다. 행은 위에서부터 아래까지 번호가 매겨져 있고,

www.acmicpc.net

 

 

 


백준 14391번 문제 종이 조각


문제


 

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율
2 초 512 MB 5436 2972 2201 55.820%

문제

영선이는 숫자가 쓰여 있는 직사각형 종이를 가지고 있다. 종이는 1×1 크기의 정사각형 칸으로 나누어져 있고, 숫자는 각 칸에 하나씩 쓰여 있다. 행은 위에서부터 아래까지 번호가 매겨져 있고, 열은 왼쪽부터 오른쪽까지 번호가 매겨져 있다.

영선이는 직사각형을 겹치지 않는 조각으로 자르려고 한다. 각 조각은 크기가 세로나 가로 크기가 1인 직사각형 모양이다. 길이가 N인 조각은 N자리 수로 나타낼 수 있다. 가로 조각은 왼쪽부터 오른쪽까지 수를 이어 붙인 것이고, 세로 조각은 위에서부터 아래까지 수를 이어붙인 것이다.

아래 그림은 4×4 크기의 종이를 자른 한 가지 방법이다.

각 조각의 합은 493 + 7160 + 23 + 58 + 9 + 45 + 91 = 7879 이다.

종이를 적절히 잘라서 조각의 합을 최대로 하는 프로그램을 작성하시오.

입력

첫째 줄에 종이 조각의 세로 크기 N과 가로 크기 M이 주어진다. (1 ≤ N, M ≤ 4)

둘째 줄부터 종이 조각이 주어진다. 각 칸에 쓰여 있는 숫자는 0부터 9까지 중 하나이다.

출력

영선이가 얻을 수 있는 점수의 최댓값을 출력한다.

예제 입력 1

2 3
123
312

예제 출력 1

435

예제 입력 2

2 2
99
11

예제 출력 2

182

예제 입력 3

4 3
001
010
111
100

예제 출력 3

1131

예제 입력 4

1 1
8

예제 출력 4

8
 

 

 


과정 생각해보기 & 오답


 

핵심 생각은 아래와 같다

가로와 세로를 각각 0, 1로 구분하는 것이다

(다만 비트마스킹 연산이 아직 생소하기 때문에 boolean값으로 설정하여 문제를 풀 것이다)

 

이후 이 블로그를 참고했다

 

이 방법은 재귀를 이용한 방법이고, 비트마스킹으로도 풀 수 있다

실제 백준 사이트는 이 문제를 비트마스킹 카테고리로 나누기도 했다

 

사실 이 방법도 가로 true, 세로 false로 설정하여 푸는 것은 같으므로, 단순하게 생각하면 booelan 값 대신 0과 1을 넣으면 비트마스킹 풀이가 된다(다만 연산 표현 방법이 달라서 매우 생소하게 느껴진다)

이 블로그에 재귀, 비트마스킹 풀이가 모두 담겨있으니 참고하면 좋을 듯하다

 

 

 

https://gimbalja.tistory.com/326

 

비트마스킹

비트마스킹(bit masking) 컴퓨터의 최소 단위 bit는 binary digit의 약자로, 이진수를 뜻한다. 1은 비트가 켜져있다, 0은 비트가 꺼져있다고도 표현한다. 비트 연산자

gimbalja.tistory.com

여기에 비트마스킹 개념을 보기 쉽게 정리하고 있다

 

 

 

 


정답 인정 코드


 
 
1) 재귀
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import java.io.*;
import java.util.*;
 
public class Main {
 
    static int n, m;
    static int max = Integer.MIN_VALUE;
    static int[][] paper;
    static boolean[][] visited;
    static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        
        n = Integer.parseInt(st.nextToken());
        m = Integer.parseInt(st.nextToken());
        paper = new int[n][m];
        visited = new boolean[n][m];
        
        for(int i = 0; i < n; i++) {
            String str = br.readLine();
            for(int j = 0; j < m; j++) {
                paper[i][j] = str.charAt(j)-'0';
            }
        }
 
//        System.out.println(Arrays.deepToString(paper));
        DFS(00);
        bw.write(max+"");
        bw.flush();
        bw.close();
    }
 
    static void DFS(int depth, int width){
//        System.out.println(Arrays.deepToString(visited));
        
        if(depth == n) {    // 모든 깊이(세로) 탐색 완료->최댓값 출력
            getMax();
            return;
        }
        
        if(width == m) {    // 한 줄 다 확인했으면
            DFS(depth+10);    // 다음행 1열로
            return;
        }
        
        // 가로
        visited[depth][width] = false;    // 0 표시
        DFS(depth, width+1);
        // 세로
        visited[depth][width] = true;    // 1 표시
        DFS(depth, width+1);
    }
    
    static void getMax() {
        int num = 0;
        int temp = 0;
        
        // 가로
        for(int i = 0; i < n; i++) {
            temp = 0;
            for(int j = 0; j < m; j++) {
                if(!visited[i][j]) {    // 0 -> 가로
                    temp *= 10;    // 더해질 때마다 자릿수 늘려주기
                    temp += paper[i][j];
                }else {
                    num += temp;
                    temp = 0;    // 세로니까 초기화
                }
            }
            num += temp;
        }
        // 세로
        for(int i = 0; i < m; i++) {
            temp = 0;
            for(int j = 0; j < n; j++) {
                if(visited[j][i]) {    // 1 -> 세로
                    temp *= 10;    // 더해질 때마다 자릿수 늘려주기
                    temp += paper[j][i];
                }else {
                    num += temp;
                    temp = 0;    // 가로니까 초기화
                }
            }
            num += temp;
        }
        
        max = Math.max(num, max);
//        System.out.println("max : "+max);
    }
}
 
cs

 

 

2) 비트마스킹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import java.io.*;
import java.util.*;
 
public class Main {
 
    static int n, m;
    static int max = Integer.MIN_VALUE;
    static int[][] paper;
    static boolean[][] visited;
    static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        
        n = Integer.parseInt(st.nextToken());
        m = Integer.parseInt(st.nextToken());
        paper = new int[n][m];
        visited = new boolean[n][m];
        
        for(int i = 0; i < n; i++) {
            String str = br.readLine();
            for(int j = 0; j < m; j++) {
                paper[i][j] = str.charAt(j)-'0';
            }
        }
 
        getMax();
        bw.write(max+"");
        bw.flush();
        bw.close();
    }
 
    static void getMax() {
        // 2^n*m 
        for(int t = 0; t < (1<<(n*m)); t++) {
            int num = 0;
            // 0 -> 가로
            for(int i = 0; i < n; i++) {
                int temp = 0;
                for(int j = 0; j < m; j++) {
                    if((t&(1<<(i*m+j))) == 0) {    // 해당 자리의 비트가 꺼져있다면 == 0이라면 == 가로라면
                        temp *= 10;    // 더해질 때마다 자릿수 늘려주기
                        temp += paper[i][j];
                    }else {
                        num += temp;
                        temp = 0;    // 세로니까 초기화
                    }
                }
                num += temp;
            }
            // 1 -> 세로
            for(int i = 0; i < m; i++) {
                int temp = 0;
                for(int j = 0; j < n; j++) {
                    if((t&(1<<j*m+i)) != 0) {    // 해당 자리의 비트가 켜있다면 == 1이라면 == 세로라면
                        temp *= 10;    // 더해질 때마다 자릿수 늘려주기
                        temp += paper[j][i];
                    }else {
                        num += temp;
                        temp = 0;    // 가로니까 초기화
                    }
                }
                num += temp;
            }
            max = Math.max(num, max);
        }
 
    }
}
cs

 

 


갈수록 어려운 건 착각인가..........^^

내일 이 문제 한 번 더 살펴봐야겠다 (완료!)

 

반응형
Comments