https://www.acmicpc.net/problem/21608
21608번: 상어 초등학교
상어 초등학교에는 교실이 하나 있고, 교실은 N×N 크기의 격자로 나타낼 수 있다. 학교에 다니는 학생의 수는 N2명이다. 오늘은 모든 학생의 자리를 정하는 날이다. 학생은 1번부터 N2번까지 번호
www.acmicpc.net
난이도: solved.ac 골드 5
구현 방법
문제에서 주어진 세 가지 조건 그대로 구현하면 되는 문제다.
먼저 첫 번째 조건을 살펴보자.
"비어있는 칸 중에서 좋아하는 학생이 인접한 칸에 가장 많은 칸으로 자리를 정한다."
이를 구현하기 위해 학생들을 배치할 공간 board[N][N]을 만들고, board가 0인 모든 빈자리를 기준으로 인접한 상하좌우 캉을 탐색해 좋아하는 학생이 몇 명 있는지 파악한다.
그리고 두 번째 조건
"1을 만족하는 칸이 여러 개이면, 인접한 칸 중에서 비어있는 칸이 가장 많은 칸으로 자리를 정한다."
각 칸들의 인접한 칸 중 비어있는 칸이 몇 개인지 빠르게 파악하기 위해 emptys라는 2차원 배열을 만들어 각 칸에 인접한 칸 중 빈 칸이 몇 개인지 적어주었다.
따라서 새로 탐색한 칸의 인접한 자리에 앉은 좋아하는 학생의 수가 이전에 탐색한 칸들 중 인접한 칸에 앉은 좋아하는 학생의 수의 최대값과 같다면 인접한 빈 칸의 개수를 비교해 새로 탐색한 칸의 인접한 빈 칸이 더 많다면 현재 찾고 있는 앉힐 자리를 새로 갱신해주었다.
그리고 세 번째 조건
"2를 만족하는 칸도 여러 개인 경우에는 행의 번호가 가장 작은 칸으로, 그러한 칸도 여러 개이면 열의 번호가 가장 작은 칸으로 자리를 정한다."
이 조건은 그냥 탐색할 때 맨 왼쪽 위 자리부터 탐색하면 자연스럽게 만족하게 된다.
따라서 세 번째 조건은 없는 셈치고 구현했다.
코드
#include <bits/stdc++.h> using namespace std; int N; int board[20][20], emptys[20][20], prefer[401][4]; int dy[4] = { 1,0,-1,0 }, dx[4] = { 0,1,0,-1 }; // board: 배정할 자리 // emptys: 비어있는 칸의 개수 (empty seats) // prefer: 각 학생들의 선호 번호 저장 void emptys_init(); void find_seat(int student); int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> N; emptys_init(); int student, i, j, k; for (i = 0; i < N * N; i++) { cin >> student; for (j = 0; j < 4; j++) { cin >> prefer[student][j]; } find_seat(student); } int ans = 0; // 각 자리별로 해당 학생과 인접한 칸에 앉은 좋아하는 학생의 수를 파악해 만족도 조사 for (i = 0; i < N; i++) { for (j = 0; j < N; j++) { int cnt = 0; // 인접한 칸에 앉은 학생 중 좋아하는 학생의 수 for (int dir = 0; dir < 4; dir++) { int ny = i + dy[dir]; int nx = j + dx[dir]; if (ny < 0 || ny >= N || nx < 0 || nx >= N) continue; for (k = 0; k < 4; k++) { if (board[ny][nx] == prefer[board[i][j]][k]) { cnt++; break; } } } if (cnt == 1) ans++; else if (cnt == 2) ans += 10; else if (cnt == 3) ans += 100; else if (cnt == 4) ans += 1000; } } cout << ans; } void emptys_init() { // 꼭지점 emptys[0][0] = 2; emptys[0][N - 1] = 2; emptys[N - 1][0] = 2; emptys[N - 1][N - 1] = 2; for (int i = 1; i < N - 1; i++) { emptys[0][i] = 3; // 위쪽 모서리 emptys[N - 1][i] = 3; // 아래쪽 모서리 emptys[i][0] = 3; // 왼쪽 모서리 emptys[i][N - 1] = 3; // 오른쪽 모서리 for (int j = 1; j < N - 1; j++) emptys[i][j] = 4; // 안쪽 } } void find_seat(int student) { int i, j, k, max = -1; // 인접한 자리에 있는 좋아하는 학생의 수 최댓값 int y, x, emptyseat; // y, x : 배치할 자리의 후보 좌표 // emptyseat: 후보 좌표에서 인접한 자리 중 비어있는 곳의 개수 for (i = 0; i < N; i++) { for (j = 0; j < N; j++) { // 이미 자리가 있는 경우는 pass if (board[i][j]) continue; int cnt = 0; // 인접한 자리에 좋아하는 학생이 몇 명이 있는지 for (int dir = 0; dir < 4; dir++) { int ny = i + dy[dir]; int nx = j + dx[dir]; if (ny < 0 || ny >= N || nx < 0 || nx >= N) continue; for (k = 0; k < 4; k++) { if (board[ny][nx] == prefer[student][k]) { cnt++; break; } } } if (cnt > max) { // 현재의 자리에서 인접한 자리에 좋아하는 학생의 수가 더 많을 때 max = cnt; // 후보 자리 새로 갱신 y = i; x = j; emptyseat = emptys[y][x]; } else if (cnt == max) { // 인접한 자리에 있는 좋아하는 학생의 수가 기존 후보 자리와 같은 경우 // 인접한 자리 중 비어있는 자리가 더 많을 때 후보 새로 갱신 if (emptys[i][j] > emptyseat) { y = i; x = j; emptyseat = emptys[y][x]; } } } } board[y][x] = student; // 자리 확정 // 인접한 칸들 emptys 하나씩 감소시키기 for (i = 0; i < 4; i++) { int ny = y + dy[i]; int nx = x + dx[i]; if (ny < 0 || ny >= N || nx < 0 || nx >= N) continue; emptys[ny][nx]--; } }
성능
