본문으로 바로가기

행렬 1080

category ps/그리디 알고리즘 2021. 10. 25. 22:51

문제 해설 및 주의사항

문제

0과 1로만 이루어진 행렬 A와 행렬 B가 있다. 이때, 행렬 A를 행렬 B로 바꾸는데 필요한 연산의 횟수의 최솟값을 구하는 프로그램을 작성하시오.

행렬을 변환하는 연산은 어떤 3×3크기의 부분 행렬에 있는 모든 원소를 뒤집는 것이다. (0 → 1, 1 → 0)

입력

첫째 줄에 행렬의 크기 N M이 주어진다. N과 M은 50보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에는 행렬 A가 주어지고, 그 다음줄부터 N개의 줄에는 행렬 B가 주어진다.

출력

첫째 줄에 문제의 정답을 출력한다. 만약 A를 B로 바꿀 수 없다면 -1을 출력한다.

 


 

풀이

※ 0 -> 1 로 바꾸는 문제들

- flip 하는 연산을 다룰 때, 명심해야 할 점은 연산은 0번 하거나 1번 할 때만 의미가 있다. 2번 하면 원래대로 돌아오기 때문이다.

 

1. 총 연산

- 2^(NM) 이므로(N <= 50, M <= 50), 브루트 포스는 절대 불가능하다.

 

2. 맨 왼쪽 상단 칸만을 보자.- 맨 왼쪽 상단 칸(0,0) 을 바꿀 수 있는 방법은 하나밖에 없다. 이것을 결정하고 (0, 1)을 결정하고..

 


풀이코드

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <tuple>
using namespace std;
// 행렬 1080

const int MAX = 50;

int from[MAX][MAX];
int to[MAX][MAX];

void flip(int x, int y){
	for(int i = x - 1; i <= x + 1; i++){
		for(int j = y - 1; j <= y + 1; j++){
			from[i][j] = 1 - from[i][j];
		}
	}
}

int main(){
	
	// 입력
	int n, m;
	scanf("%d %d", &n, &m);
	for(int i = 0; i < n; i++){
		for(int j = 0 ; j < m; j++){
			scanf("%1d", &from[i][j]);
		}
	}
	 for (int i=0; i<n; i++) {
 		for (int j=0; j<m; j++) {
			scanf("%1d",&to[i][j]);
 		}
 	}
	int ans = 0;
	for(int i = 0; i < n - 2; i++){
		for(int j = 0; j < m - 2; j++){
			// 3x3 박스의 맨 왼쪽 부분만을 비교하여, 같으면 두고, 다르면 flip 한다.
			if(from[i][j] != to[i][j]){
				ans += 1;
				flip(i + 1, j + 1);
			}
		}
	}
	
	// 전체 비교
	for (int i=0; i<n; i++) {
 		for (int j=0; j<m; j++) {
 			if (from[i][j] != to[i][j]) {
 				printf("-1\n");
 				return 0;
 			}
 		}
 	}	
	
 	printf("%d\n",ans);
}

 


퇴고

 

반응형

'ps > 그리디 알고리즘' 카테고리의 다른 글

가장 긴 증가하는 부분 수열 2 (LIS)  (0) 2021.10.31
[우선순위 큐] 순회 강연 2109  (0) 2021.10.31
[priority queue] [multiset] 보석 도둑 1202  (0) 2021.10.28
회의실 배정 1931  (0) 2021.10.25
두 동전 11047  (0) 2021.10.25