백준/구현

백준 21608 c++ "상어 초등학교" -PlusUltraCode-

PlusUltraCode 2025. 6. 3. 20:45

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

 

[필자 사고]

구현 문제다

문제에서 주어진 조건은 3가지다

다행이 까탈스러운 조건이 아니라 한번에 우선순위 큐를 이용하여 정렬 가능한 요구조건이다.

 

아래는 자세한 코드해설이다.

[코드 해설]

1. Input 함수

  • 입력으로부터 격자의 크기 N을 받고, N×N 크기의 2차원 벡터 arr를 0으로 초기화합니다.
  • 이후 N×N 명의 학생(또는 사용자) 정보가 들어오는데, 각 학생마다 자신의 번호 me와 좋아하는 친구 4명 번호(a, b, c, d)를 입력받습니다.
  • 이 정보를 likes 벡터에 저장하며, 학생 번호를 키로 하여 인덱스를 빠르게 찾을 수 있도록 myMap이라는 맵에 저장합니다.

2. isInside 함수

  • 좌표 (sero, garo)가 격자 내에 존재하는지 판단하는 함수입니다.
  • 좌표가 0 이상 N 미만 범위 내에 있으면 true, 아니면 false를 반환합니다.

3. getCountLikeFriend 함수

  • 특정 위치 (nextSero, nextGaro)에 앉아 있는 학생 번호가, 인자로 받은 friendNode가 좋아하는 친구 목록에 포함되는지를 확인합니다.
  • 그 칸이 빈 칸이면 0을 반환하고, 친구 목록에 있으면 1, 아니면 0을 반환합니다.

4. checkLikeFriend 함수

  • 현재 좋아하는 친구 정보를 가진 학생이 앉을 수 있는 최적의 자리를 찾기 위해 전체 격자를 탐색합니다.
  • 이미 사람이 앉아있는 칸은 건너뛰고, 빈 칸에 대해 인접한 4방향을 검사합니다.
  • 인접 칸 중 빈 칸 개수와 친구가 앉아있는 칸 개수를 각각 센 후, 우선순위 큐에 위치 정보와 함께 저장합니다.
  • 우선순위 큐는 나중에 조건에 맞는 최적의 자리를 쉽게 꺼낼 수 있게 해줍니다.

5. checkCountFriend 함수

  • 완성된 자리 배치 후, 특정 학생이 앉은 칸 (sero, garo)의 인접한 4칸을 살펴 좋아하는 친구가 몇 명 앉아있는지 세고, 그 수에 따라 점수를 반환합니다.
  • 0명이면 0점, 1명 1점, 2명 10점, 3명 100점, 4명 1000점으로 가중치를 다르게 둡니다.
  • 이 점수 합이 최종 만족도 점수로 활용됩니다.

6. Game_Start 함수

  • likes 벡터 순서대로 학생들을 자리 배치합니다.
  • 각 학생마다 우선순위 큐를 초기화하고, checkLikeFriend 함수로 그 학생에게 최적의 자리를 찾습니다.
  • 우선순위 큐의 top 위치가 해당 학생이 앉을 자리입니다.
  • 모든 학생 배치가 끝난 후, 전체 자리에서 학생별로 인접 친구 수에 따른 점수를 계산하여 합산합니다.
  • 최종 점수를 출력합니다.

7. main 함수

  • 입출력 속도 최적화 설정 후, Input 함수와 Game_Start 함수를 순서대로 호출하여 프로그램을 실행합니다.

[소스 코드]

#include <iostream>
#include <vector>
#include <queue>
#include <map>
using namespace std;

struct friendNode {
    int me;
    vector<int> friends;
};

struct Node {
    int sero;
    int garo;
    int count;       // 인접 친구 수
    int emptyCount;  // 인접 빈 칸 수
};

int dy[4] = { 0, -1, 0, 1 };
int dx[4] = { 1, 0, -1, 0 };

struct cmp {
    bool operator()(const Node& a, const Node& b) {
        if (a.count != b.count)
            return a.count < b.count;          // count 큰게 우선
        if (a.emptyCount != b.emptyCount)
            return a.emptyCount < b.emptyCount; // emptyCount 큰게 우선
        if (a.sero != b.sero)
            return a.sero > b.sero;            // sero 작은게 우선
        return a.garo > b.garo;                // garo 작은게 우선
    }
};

int N;
vector<vector<int>> arr;
vector<friendNode> likes;
priority_queue<Node, vector<Node>, cmp> pq;
map<int, int> myMap;

void Input() {
    cin >> N;
    arr.assign(N, vector<int>(N, 0));

    for (int i = 0; i < N * N; i++) {
        int me;
        cin >> me;
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        likes.push_back({ me, {a, b, c, d} });
        myMap[me] = i;
    }
}

bool isInside(int sero, int garo) {
    return sero >= 0 && sero < N && garo >= 0 && garo < N;
}

// 해당 칸에 있는 번호가 node 친구 목록에 있는지 확인
int getCountLikeFriend(const friendNode& node, int nextSero, int nextGaro) {
    int friendNumber = arr[nextSero][nextGaro];
    if (friendNumber == 0) return 0;
    for (int f : node.friends) {
        if (f == friendNumber) return 1;
    }
    return 0;
}

void checkLikeFriend(const friendNode& node) {
    for (int i = 0; i < N; i++) {
        for (int k = 0; k < N; k++) {
            if (arr[i][k] != 0) continue; // 이미 자리 있음

            int emptyCount = 0;
            int friendCount = 0;

            for (int j = 0; j < 4; j++) {
                int nextSero = i + dy[j];
                int nextGaro = k + dx[j];
                if (!isInside(nextSero, nextGaro)) continue;

                if (arr[nextSero][nextGaro] == 0) {
                    emptyCount++;
                }
                else {
                    friendCount += getCountLikeFriend(node, nextSero, nextGaro);
                }
            }
            pq.push({ i, k, friendCount, emptyCount });
        }
    }
}

int checkCountFriend(const friendNode& fNode, int sero, int garo) {
    int count = 0;
    for (int i = 0; i < 4; i++) {
        int nextSero = sero + dy[i];
        int nextGaro = garo + dx[i];
        if (!isInside(nextSero, nextGaro)) continue;

        for (int num : fNode.friends) {
            if (num == arr[nextSero][nextGaro]) {
                count++;
                break;
            }
        }
    }

    if (count == 0) return 0;
    if (count == 1) return 1;
    if (count == 2) return 10;
    if (count == 3) return 100;
    if (count == 4) return 1000;
    return 0;
}

void Game_Start() {
    for (int i = 0; i < likes.size(); i++) {
        // pq 초기화
        pq = priority_queue<Node, vector<Node>, cmp>();

        const friendNode& node = likes[i];
        checkLikeFriend(node);
        Node pqNode = pq.top();
        arr[pqNode.sero][pqNode.garo] = node.me;
    }

    int resultSum = 0;
    for (int i = 0; i < N; i++) {
        for (int k = 0; k < N; k++) {
            int nowNumber = arr[i][k];
            int index = myMap[nowNumber];
            const friendNode& fNode = likes[index];
            resultSum += checkCountFriend(fNode, i, k);
        }
    }

    cout << resultSum << "\n";
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    Input();
    Game_Start();

    return 0;
}