알고리즘/문제 풀이

1926번: 그림

Themion 2021. 12. 10. 17:29

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

 

1926번: 그림

어떤 큰 도화지에 그림이 그려져 있을 때, 그 그림의 개수와, 그 그림 중 넓이가 가장 넓은 것의 넓이를 출력하여라. 단, 그림이라는 것은 1로 연결된 것을 한 그림이라고 정의하자. 가로나 세로

www.acmicpc.net

배열의 각 성분을 노드, 성분이 상하좌우로 인접한 것을 에지라고 하면 이 문제는 그래프를 순회하며 그래프의 개수와 노드의 수가 가장 많은 그래프를 찾는 문제가 된다. 

- 너비 우선 탐색

#include <iostream>
#include <queue>

using namespace std;

typedef pair<int, int> coord;

#define _y first
#define _x second

#define MAX_N 500

// 그림이 그려진 도화지
bool map[MAX_N][MAX_N];
// N, M: 도화지의 크기, ans: 도화지 안의 그림의 최대 크기
int N, M, ans = 0;

void set_max(int &a, int b) { a = a > b ? a : b; }
// 좌표 + 좌표
coord operator+(coord a, int add[2]) {
    return {a._y + add[0], a._x + add[1]};
}

// 주어진 좌표가 도화지 안의 좌표라면 true, 아니라면 false
bool valid(coord c) {
    return c._y >= 0 && c._y < N && c._x >= 0 && c._x < M;
}

// 주어진 좌표를 포함하는 그림을 탐색
bool bfs(int y, int x) {
    // 한 좌표와 인접한 좌표를 구할 때 사용
    int add[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    // 그림의 크기
    int size = 0;
    // bfs에 사용할 큐
    queue<coord> q;
    // 가장 먼저 탐색한 좌표를 큐에 넣고 도화지에서 삭제
    q.push({y, x});
    map[y][x] = false;

    while (!q.empty()) {
        // 큐 안의 모든 좌표에 대해
        coord c = q.front();
        q.pop();

        // 좌표 수만큼 size에 1씩 더한다
        size += 1;

        for (int i = 0; i < 4; i++) {
            // 각 좌표와 인접한 좌표에 대해
            coord c_ = c + add[i];
            // 좌표가 도화지 안의 좌표이고 아직 탐색하지 않은 그림이라면
            if (valid(c_) && map[c_._y][c_._x]) {
                // 좌표를 큐에 넣고 그림에서 삭제
                q.push(c_);
                map[c_._y][c_._x] = false;
            }
        }
    }

    // 그림의 최대 사이즈를 갱신
    set_max(ans, size);
    // 연산의 압축을 위해 true를 반환
    return true;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    
    int cnt = 0;

    // 문제의 조건을 입력받는다
    cin >> N >> M;
    for (int i = 0; i < N; i++) for (int j = 0; j < M; j++)
        cin >> map[i][j];

    // 각 좌표에 대해 그래프가 존재한다면 bfs를 이용한 탐색
    for (int i = 0; i < N; i++) for (int j = 0; j < M; j++) 
        if (map[i][j]) cnt += bfs(i, j);

    // 그림의 수와 그림의 최대 크기를 출력
    cout << cnt << '\n' << ans << '\n';

    return 0;
}

 

- 깊이 우선 탐색

#include <cstdio>

bool map[500][500];
int N, M, ans = 0;

void set_max(int& a, int b) { a = a > b ? a : b; }

// 주어진 좌표가 도화지 안의 좌표라면 true, 아니라면 false
bool valid(int y, int x)
{ return y >= 0 && y < N && x >= 0 && x < M && map[y][x]; }

// 그래프를 탐색하며 그래프의 크기를 계산
bool dfs(int y, int x, int& size) {
    // 한 좌표와 인접한 좌표를 구할 때 사용
    int add[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    
    // 현재 위치를 탐색
    map[y][x] = false;
    size += 1;

	// 인접한 위치에 대해 dfs를 실행
    for (int i = 0; i < 4; i++) {
        int y_ = y + add[i][0], x_ = x + add[i][1];
        if (valid(y_, x_)) dfs(y_, x_, size);
    }

	// 그래프의 최대 크기 갱신
    set_max(ans, size);
    // 연산의 압축을 위해 true를 반환
    return true;
}

// 
bool dfs(int y, int x) {
    int temp = 0;
    return dfs(y, x, temp);
}

int main() {
    int cnt = 0;
    
    // 문제의 조건을 입력받는다
    scanf("%d %d", &N, &M);
    for (int i = 0; i < N; i++) for (int j = 0; j < M; j++) {
        scanf("%d", &cnt);
        map[i][j] = cnt != 0;
    }

	// 그래프의 개수를 초기화한 뒤
    cnt = 0;
    // 각 좌표에 대해 그래프가 존재한다면 dfs를 이용한 탐색
    for (int i = 0; i < N; i++) for (int j = 0; j < M; j++) 
        if (map[i][j]) cnt += dfs(i, j);

    // 그림의 수와 그림의 최대 크기를 출력
    printf("%d\n%d\n", cnt, ans);

    return 0;
}