본문 바로가기

백준/그리디

백준 1080 - 행렬

728x90

www.acmicpc.net/problem/1080

 

1080번: 행렬

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

www.acmicpc.net

한번 연산하면 3X3 부분행렬의 숫자가 0이면 1, 1이면 0으로 바뀐다고 할 때 필요한 연산의 최솟값을 구하는 문제이다.

 

3X3 부분행렬만 연산이 가능하므로 행렬의 전체 인덱스를 벗어나서 연산하는 일이 없도록 해야한다.

 

 

부분행렬 연산은 현재 인덱스 (i, j)에 대해 다음 범위로 계산한다고 하자.

i, j

i, j + 1

i, j + 2

i + 1, j

i  + 1, j + 1

i + 1, j + 2

i + 2, j

i + 2, j + 1

i + 2, j + 2

이 문제는 matrix[i][j]의 원소가 한번 원하는 상태로 놓이면 더이상 바꾸지 않는 것이 최적해에 도달 할 수 있는 조건에 충족됨을 이해해야 한다.

 

자연수 $x <= 2, y <= 2$ 일 때

해당 인덱스(i, j)에 대해 원하는 상태로 만들고 나면 다음 인덱스(i + x, j + y)로 넘어갈 때 인덱스(i, j)는 인덱스 (i + x, j + y)에 대해 영향을 미치지 않는다는 점에서 탐욕적 속성을 만족한다.

 

그리고 다음 인덱스로 넘어간 시점에서 인덱스(i, j)는 최적 부분 구조를 만족했으므로 해당 인덱스의 값만 신경쓰는 그리디 방법을 적용할 수 있음을 알 수 있다.

 

이 전략에 따라 반복문을 돌며 matrix[i][j]의 원소를 바꿀지 말지 결정하는 문제로 바꾸어 해당 인덱스의 상태가 구하려는 행렬의 인덱스의 상태와 같아지도록 연산을 수행한다.

 

물론 연산은 3X3 부분행렬에 대해 적용해야 한다는 사실을 잊어서는 안되겠다.

 

전체 코드

#include <bits/stdc++.h>

using namespace std;

int cur[50][50];
int goal[50][50];
int click[2] {1, 0};

int r, c;

bool issame();

int main()
{
    ios::sync_with_stdio(false); cin.tie(nullptr);
    
    (cin >> r >> c).get();
    
    for (int i = 0; i < r; ++i)
    {
        for (int j = 0; j < c; ++j)
            cur[i][j] = cin.get() - 48;
        cin.get();
    }
    
    for (int i = 0; i < r; ++i)
    {
        for (int j = 0; j < c; ++j)
            goal[i][j] = cin.get() - 48;
        cin.get();
    }
    
    if (r < 3 || c < 3)
    {
        if (!issame())
        {
            cout << -1;
            return 0;
        }
        
        cout << 0;
        return 0;
    }
    
    int cnt = 0;
    
    for (int i = 0; i <= r - 3; ++i)
    {
        for (int j = 0; j <= c - 3; ++j)
        {
            if (cur[i][j] == goal[i][j])
                continue;
            
            ++cnt;
            for (int k = i; k < i + 3; ++k)
                for (int s = j; s < j + 3; ++s)
                    cur[k][s] = click[cur[k][s]];
        }
    }
    
    if (issame())
        cout << cnt;
    else
        cout << -1;
}

bool issame()
{
    for (int i = 0; i < r; ++i)
        for (int j = 0; j < c; ++j)
            if (cur[i][j] != goal[i][j])
                return false;
    return true;
}

이 코드에서는 전체 행렬의 크기가 3X3이 안될 때 연산없이 두 행렬이 같은지 Base case를 체크했는데 굳이 안해도 다음 for문의 조건에 의해 올바르게 답을 구할 수 있다.

 

728x90

'백준 > 그리디' 카테고리의 다른 글

백준 17420 - 깊콘이 넘쳐흘러  (0) 2020.07.09
백준 1082 - 방 번호  (0) 2020.07.08
백준 13458 - 시험 감독  (0) 2020.06.20
백준 1758 - 알바생 강호  (0) 2020.06.06
[ICPC] 백준 17521 - Byte Coin  (0) 2020.06.05