알고리즘/백준
21608_상어 초등학교
베리영young
2024. 6. 11. 13:08
사용 알고리즘: 구현 빡!
사용 언어: java
package src.SamsungA;
import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
/**
* 이런 식으로 출력된다;
* 0 -> 4 2 5 1 7
* 1 -> 3 1 9 4 5
* 2 -> 9 8 1 2 3
* 3 -> 8 1 9 3 4
* 4 -> 7 2 3 4 8
* 5 -> 1 9 2 5 7
* 6 -> 6 5 2 3 4
* 7 -> 5 1 9 2 8
* 8 -> 2 9 3 1 4
*/
public class Main_bj_21608_상어초등학교 {
static int n;
static int[][] map;
static ArrayList<ArrayList<Integer>> sittingOrder;
static int[] dx = {1,-1,0,0};
static int[] dy = {0,0,1,-1};
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
n = Integer.parseInt(br.readLine());
map = new int[n][n];
sittingOrder = new ArrayList<>(n*n);
for (int i = 0; i < n*n; i++) {
st = new StringTokenizer(br.readLine());
ArrayList<Integer> tmp = new ArrayList<>();
for (int j = 0; j < 5; j++) {
tmp.add(Integer.parseInt(st.nextToken()));
}
sittingOrder.add(tmp);
}
//
map[1][1] = sittingOrder.get(0).get(0); //첫 번째 학생은 무조건 여기
for (int studentNum = 1; studentNum < n*n; studentNum++) {
//3. 조건을 만족하는 칸이 여러 개라면, 행,열 번호가 가장 작은 칸으로 보내기 => Priority Queue 사용
PriorityQueue<Point> pq = new PriorityQueue<>(((o1, o2) -> {
if (o1.x != o2.x) {
return o1.x - o2.x; // x값 기준으로 비교
}
else {
return o1.y - o2.y; // x값이 같으면 y값 기준으로 비교
}
}));
//1. 빈 칸 중, 좋아하는 4학생이 인접 칸에 가장 많은 칸으로 자리 정하기.
pq = assignSeatsBasedLikeStudents(studentNum, pq);
//2. 인접 칸 중 빈 칸이 가장 많은 칸
pq = assignSeatsBasedEmpties(pq);
//자리 세팅!
Point assignHere = pq.peek(); //첫 번째
map[assignHere.x][assignHere.y] = sittingOrder.get(studentNum).get(0);
}
// for (int i = 0; i < n; i++) {
// System.out.println(Arrays.toString(map[i]));
// }
System.out.println(printSatisfaction());
}
private static int printSatisfaction() {
int satisfaction = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
int buddies4dir = howManyBuddiesNearHere(i,j); //4방에 친구 몇 명?
satisfaction += buddiesScore(buddies4dir); //친구 수만큼 만족도 점수 반영 => 남들은 어떻게 했을까? 너무 코드가 별론데..
}
}
return satisfaction;
}
private static int howManyBuddiesNearHere(int i, int j) { //4방에 친구 몇 명?
int me = map[i][j];
int[] myBuddies = findBuddiesBaseMe(me);
int buddyCnt = 0;
for (int d = 0; d < 4; d++) {
int nx = i + dx[d];
int ny = j + dy[d];
if (nx>=0 && nx<n && ny>=0 && ny<n) {
if( nearBuddy(map[nx][ny], myBuddies) ) buddyCnt++;
}
}
return buddyCnt;
}
private static boolean nearBuddy(int buddyNum, int[] myBuddies) {
for (int buddy : myBuddies) {
if (buddy == buddyNum) return true;
}
return false;
}
private static int[] findBuddiesBaseMe(int me) {
int[] buddies = new int[4];
for (int i = 0; i < n*n; i++) {
if (sittingOrder.get(i).get(0) == me) {
for (int j = 1; j <= 4 ; j++) {
buddies[j-1] = sittingOrder.get(i).get(j);
}
break;
}
}
return buddies;
}
private static int buddiesScore(int buddies) { //친구 수만큼 만족도 점수 반영
if (buddies==1) return 1;
if (buddies==2) return 10;
if (buddies==3) return 100;
if (buddies==4) return 1000;
return 0;
}
private static PriorityQueue<Point> assignSeatsBasedEmpties(PriorityQueue<Point> pq) {
PriorityQueue<Point> tmpPq = new PriorityQueue<>(((o1, o2) -> {
if (o1.x != o2.x) return o1.x - o2.x;
else return o1.y - o2.y;
}));
int empties = 0;
for (Point cur : pq) {
//4방 빈 칸 찾기
int curSeatEmpties = findEmpties4dir(cur);
if ( curSeatEmpties > empties ) {
empties = curSeatEmpties;
tmpPq.clear();
tmpPq.offer(cur);
} else if ( curSeatEmpties == empties ) {
tmpPq.offer(cur);
}
}
return tmpPq;
}
private static int findEmpties4dir(Point cur) {
int empties = 0;
for (int d = 0; d < 4; d++) {
int nx = cur.x + dx[d];
int ny = cur.y + dy[d];
if (nx>=0 && nx<n && ny>=0 && ny<n) {
if (map[nx][ny] == 0) empties++;
}
}
return empties;
}
private static PriorityQueue<Point> assignSeatsBasedLikeStudents(int studentNum, PriorityQueue<Point> pq) {
int buddyCnt = 0; //여태 찾은 인접 칸에 친구가 가장 많은 자리의 친구 수
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (map[i][j] == 0) { //i,j 좌석이 빈 칸이면,
//4방 친구 찾기
int curSeatBuddies = findBuddies4dir(i,j, sittingOrder.get(studentNum));
if( curSeatBuddies > buddyCnt ) { //기존보다 친구가 많으면 pq 털고, 새로 넣기
buddyCnt = curSeatBuddies;
pq.clear();
pq.offer(new Point(i, j));
}
else if ( curSeatBuddies == buddyCnt ) { //기존과 같은 수면 pq에 추가만
pq.offer(new Point(i, j));
}
}
}
}
//
return pq;
}
private static int findBuddies4dir(int i, int j, ArrayList<Integer> buddies) {
int buddyCnt = 0;
int me = buddies.get(0);
for (int d = 0; d < 4; d++) {
int nx = i + dx[d];
int ny = j + dy[d];
if (nx>=0 && nx<n && ny>=0 && ny<n) {
if ( buddies.contains(map[nx][ny]) && map[nx][ny] != me ) {
buddyCnt++;
}
}
}
return buddyCnt;
}
}
느낀 점:
여기서 ArrayList<ArrayList<>>를 연습해 보려고 했다.
논리를 짠 거는 문제가 없었으나, 중간중간 정신이 나갈 뻔한 게 많았다.
논리 자체가 어려운 건 아니었지만, 복잡한 게 좀 있어서
집중해서 풀지 않았으면 못 풀었을 거 같다!
====================================
다른 사람 코드를 보니,
한 줄 입력 받고, 바로 map에서 들어갈 수 있는 좌표를 찾았음
ㄴ 이 때도 {pq에 좌표+4방향 친구 수+빈 칸 수}를 한꺼번에 입력 받아서(클래스로 만들어서) pq를 만들 때 4가지 조건을 한꺼번에 비교해서 가장 최우선이 되는 좌표가 pq 맨 앞에 오게 만듦.
seats.sort((s1, s2) -> {
if (s1.friendCnt == s2.friendCnt) { //1순위 : 좋아하는 학생이 많은 칸
if (s1.emptyCnt == s2.emptyCnt) { //2순위 : 비어있는 칸이 많은 칸
if (s1.y == s2.y) { //3순위 : 행의 번호가 가장 작은 칸
return s1.x - s2.x; //4순위 : 열의 번호가 가장 작은 칸
}
return s1.y - s2.y;
}
return s2.emptyCnt - s1.emptyCnt;
}
return s2.friendCnt - s1.friendCnt;
});
천재적인데?