포스트

백준 1080. 행렬

행렬

생각보다 어려웠던 그리디 문제

1. 간단 설명


image

0과 1로만 이루어진 행렬 A, B이 주어진다

1
2
3
000    111
000 -> 111
000    111

행렬을 뒤집으면 3x3만큼 다같이 뒤집어진다

행렬 A를 B로 바꾸는데 필요한 연산 회수의 최소값 구하기

2. 예시


예제 1)

1
2
3
4
5
6
7
8
9
3 3

111 <- 행렬 A
111
111

000 <- 행렬 B
000
000

(0,0)부터 3x3만큼 뒤집으면 한번에 A->B 변환 가능

예제 2)

1
2
3
1 1
1
0

-1

불가능?

행렬의 크기가 3보다 작으면 뒤집기 자체가 불가능한것으로 판단하는 듯 하다

크기가 3x3보다 작다면 무조건 -1을 출력하도록 세팅해야할듯함

예제 3)

1
2
3
4
5
6
7
8
9
10
11
3 4
0000
0010
0000
1001
1011
1001

0000    0111    1001
0010 -> 0101 -> 1011
0000    0111    1001

첫번째 (0,1)에서 3x3만큼, 두번째 (0,0)에서 3x3만큼 뒤집으면 가능

3. 알고리즘


첫번째 아이디어

(0,0)~(N-3, M-3)까지 가능한 경우의수를 모두 뒤집어봄

최대 (50,50) 행렬이므로, (0,0)~(47,47) 중 하나의 좌표를 잡고 뒤집고, 정답이 나올때까지 뒤집어본다

-> (47*47)!인데, 시간상 절대 2초 내에 불가능

브루트포스 외에 다른 아이디어가 필요하다

두번쨰 아이디어

왜 그리디 문제일까

왼쪽 위부터 오른쪽 아래로 순서대로 B를 만들면서 뒤집어 나가면 가능하지 않을까?

즉, 첫번째 아이디어 브루트포스처럼 랜덤한 좌표를 골라서 뒤집는것이 아니라, 좌상단부터 순서대로 맞춰가는 것

A[y][x] != B[y][x]라면 해당 좌표는 무조건 뒤집어야한다

이후 옆칸으로 이동하면 옆에서 뭔짓을 하던간에, 이 좌표는 영향을 받지 않으므로 순차적인 진행이 가능하다

1
2
3
4
5
6
7
8
9
1. N, M 입력받기
2. 행렬 A, B 입력받기
3. (0,0) 부터 (N-3, M-3)까지 A의 해당 원소를 B와 비교한다
	3-1. 만약 같다면 패스
	3-2. 같지 않다면 해당 좌표부터 3x3을 뒤집는다. 뒤집은 회수도 기록한다
	3-3. (N-3, M-3)까지 다 체크했다면 루프 종료
4. 행렬 A와 B가 같은지 체크한다
	4-1. 같다면 뒤집은 회수 출력
	4-2. 다르다면 -1 출력

필요 변수

  • int N, M : 행렬의 크기
  • vector<string> A, B : 행렬 A, B
  • int flip_cnt : 뒤집은 회수
  • bool Flip(int yy, int xx) : (yy,xx) 부터 3x3을 뒤집는 함수

4. 소스코드


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
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int N, M;
vector<string> A, B;

void Input() {
	// freopen_s(new FILE*, "input.txt", "r", stdin);
	cin >> N >> M;
	A.resize(N);
	B.resize(N);
	for (int y = 0; y < N; y++) {
		cin >> A[y];
	}
	for (int y = 0; y < N; y++) {
		cin >> B[y];
	}
}

// 배열 A의 (yy,xx)의 좌표부터 3x3을 뒤집는 함수
// 불가능하면 false를 리턴한다
bool Flip(int yy, int xx) {
	if (yy < 0 || xx < 0 || yy > N - 3 || xx > M - 3) return false;
	
	for (int y = yy; y < yy + 3; y++) {
		for (int x = xx; x < xx + 3; x++) {
			if (A[y][x] == '0') {
				A[y][x] = '1';
			}
			else {
				A[y][x] = '0';
			}
			
		}
	}

	return true;
}

int main() {
	Input();
	
	int flip_cnt = 0;

	// (0,0) ~ (N-3, M-3)까지 체크
	// N, M이 2보다 작으면 아예 뒤집기 시도 안함
	for (int y = 0; y <= N - 3; y++) {
		for (int x = 0; x <= M - 3; x++) {
			if (A[y][x] != B[y][x]) {
				Flip(y, x);
				flip_cnt++;
			}
		}
	}
	
	if (A == B) {
		cout << flip_cnt;
	}
	else {
		cout << -1;
	}


	return 0;
}

줄별로 비교하는 방법을 떠올려서 각 열을 비교하는 방식으로 도전했다가 실패했다

이후 각 원소 자체를 비교하는 방법으로 시도해서 성공

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.