Live Today
[코드트리] 색깔 폭탄 (Java) 본문
반응형
코드트리 | 코딩테스트 준비를 위한 알고리즘 정석
국가대표가 만든 코딩 공부의 가이드북 코딩 왕초보부터 꿈의 직장 코테 합격까지, 국가대표가 엄선한 커리큘럼으로 준비해보세요.
www.codetree.ai
✔️ 문제 리뷰
- 소요시간 : 3시간 30분
- 중력 작용하는 부분이 틀려서 조금 오래 걸림
- 폭탄 구역 체크를 할 때, 빨간색 폭탄은 visited 처리 유의해야 함!
✔️ 문제 풀이
- 현재 가장 큰 폭탄 묶음을 찾는다.
- 가장 큰 폭탄 묶음을 제거한다.
- 중력 작용
- 반시계 90도 회전
- 또 다시 중력 작용
- 더 이상 폭탄 묶음이 없을 때까지 반복한다.
- 최종 획득한 점수를 출력한다.
❌ 틀린 gravity() 함수
for(int j=0;j<N;j++) {
int cnt = 0;
for(int i=N-1;i>=0;i--) {
if(board[i][j] == -1) {
cnt = 0;
continue;
}
if(board[i][j] == 0) {
cnt++;
continue;
}
if(board[i][j] >= 1 && board[i][j] <= M) {
if(cnt > 0) {
board[i+cnt][j] = board[i][j];
board[i][j] = 0;
}
}
}
}
⭕ 정답 gravity() 코드
int[][] temp = new int[N][N];
for(int j=0;j<N;j++) {
int lastIdx = N-1;
for(int i=N-1;i>=0;i--) {
if(board[i][j] == 0) continue;
if(board[i][j] == -1) {
lastIdx = i;
}
temp[lastIdx--][j] = board[i][j];
}
}
board = temp;
✅ 정답 코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
static int N,M,result;
static int[][] board;
static int[] dx = {-1,1,0,0};
static int[] dy = {0,0,-1,1};
static PriorityQueue<Point> pq;
static boolean[][] visited;
static class Point implements Comparable<Point>{
int x,y;
int redCnt;
int cnt;
public Point(int x, int y, int redCnt, int cnt) {
this.x = x;
this.y = y;
this.redCnt = redCnt;
this.cnt = cnt;
}
@Override
public int compareTo(Point o) {
if(this.cnt == o.cnt) {
if(this.redCnt == o.redCnt) {
if(this.x == o.x) {
return this.y - o.y;
}
return o.x - this.x;
}
return this.redCnt - o.redCnt;
}
return o.cnt - this.cnt;
}
}
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
board = new int[N][N];
for(int i=0;i<N;i++) {
st = new StringTokenizer(br.readLine());
for(int j=0;j<N;j++) {
board[i][j] = Integer.parseInt(st.nextToken());
// 빨간색 폭탄 : -2로 표기
if(board[i][j] == 0) board[i][j] = -2;
}
} // input end
solve();
System.out.println(result);
}
private static void solve() {
while(is_possible()) {
// 1. 현재 가장 큰 폭탄 묶음 찾기
find_biggest_bomb();
// 2. 선택된 폭탄 모두 제거
int score = remove_bomb();
// 3. 중력 작용
gravity();
// 4. 반시계 90도 회전
rotate_board();
// 5. 다시 중력 작용
gravity();
result += score*score;
}
}
// 반시계 90도 회전
private static void rotate_board() {
int[][] copy = new int[N][N];
int row = N-1;
for(int j=0;j<N;j++) {
for(int i=0;i<N;i++) {
copy[row][i] = board[i][j];
}
row--;
}
board = copy;
}
private static void gravity() {
int[][] temp = new int[N][N];
for(int j=0;j<N;j++) {
int lastIdx = N-1;
for(int i=N-1;i>=0;i--) {
if(board[i][j] == 0) continue;
if(board[i][j] == -1) {
lastIdx = i;
}
temp[lastIdx--][j] = board[i][j];
}
}
board = temp;
}
private static int remove_bomb() {
Point target = pq.poll();
Queue<int[]> q = new LinkedList<>();
boolean[][] visited = new boolean[N][N];
q.add(new int[] {target.x,target.y,1});
int num = board[target.x][target.y];
visited[target.x][target.y] = true;
int count = 0;
while(!q.isEmpty()) {
int[] temp = q.poll();
int x = temp[0];
int y = temp[1];
for(int d=0;d<4;d++) {
int nx = x + dx[d];
int ny = y + dy[d];
if(!is_valid(nx, ny) || visited[nx][ny]) continue;
if(board[nx][ny] == -1) continue;
if(board[nx][ny] == -2 || board[nx][ny] == num) {
count++;
visited[nx][ny] = true;
q.add(new int[] {nx,ny});
board[nx][ny] = 0;
continue;
}
}
}
board[target.x][target.y] = 0;
return count+1;
}
private static void find_biggest_bomb() {
pq = new PriorityQueue<>();
visited = new boolean[N][N];
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
if(!visited[i][j] && board[i][j] >= 1 && board[i][j] <= M) {
visited[i][j] = true;
bfs(i,j,true);
}
}
}
}
// 폭탄 구역 체크하는 함수 -> 기준점 찾기
private static boolean bfs(int r, int c, boolean sim) {
boolean flag = false;
Queue<int[]> q = new LinkedList<>();
q.add(new int[] {r,c,1});
int num = board[r][c];
visited[r][c] = true;
int count = 1;
int redCount = 0;
// 기준점
int tx = r;
int ty = c;
while(!q.isEmpty()) {
int[] temp = q.poll();
int x = temp[0];
int y = temp[1];
// 기준점 갱신
if(board[x][y] != -2) {
if(tx < x) {
tx = x;
ty = y;
}
else if(tx == x && ty > y) {
ty = y;
}
}
for(int d=0;d<4;d++) {
int nx = x + dx[d];
int ny = y + dy[d];
if(!is_valid(nx, ny) || visited[nx][ny]) continue;
if(board[nx][ny] == -1) continue;
// 빨간색 폭탄이거나 똑같은 색깔인 경우
if(board[nx][ny] == -2 || board[nx][ny] == num) {
count++;
if(board[nx][ny] == -2) redCount++;
visited[nx][ny] = true;
q.add(new int[] {nx,ny});
continue;
}
}
}
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
if(visited[i][j]) {
if(board[i][j] == -2) visited[i][j] = false;
}
}
}
if(count >= 2) flag = true;
if(flag && sim) { // 가장 큰 폭탄 묶음 찾는 경우에만
pq.add(new Point(tx, ty, redCount,count));
}
return flag;
}
private static boolean is_possible() {
visited = new boolean[N][N];
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
if(!visited[i][j] && board[i][j] >= 1 && board[i][j] <= M) {
if(bfs(i,j,false)) return true;
}
}
}
return false;
}
private static boolean is_valid(int r, int c) {
if(r<0 || c<0 || r>=N || c>=N) return false;
return true;
}
}
'알고리즘 > 코드트리' 카테고리의 다른 글
[코드트리] 미로 타워 디펜스 (Java) (0) | 2023.09.26 |
---|---|
[코드트리] 회전하는 빙하 (Java) (0) | 2023.09.04 |
[코드트리] 술래잡기 체스 (Java) (0) | 2023.08.11 |
[코드트리] 원자 충돌 (Java) (0) | 2023.08.01 |
[코드트리] 놀이기구 탑승 (Java) (0) | 2023.07.25 |