[백준 16235] 나무 재테크(Java)

최민길(Gale)·2023년 1월 26일
1

알고리즘

목록 보기
24/172

문제 링크 : https://www.acmicpc.net/problem/16235

이 문제는 문제에 주어진 조건대로 하나하나 구현하면 됩니다.

이 문제의 포인트는 어떻게 어린 나무들을 먼저 양분을 먹게 할 것인지를 체크하는 것입니다. 저는 우선순위 큐를 나이를 기준으로 정렬되게끔 구현하여 데이터 삽입 시 자동적으로 정렬되도록 구현하였습니다. 이 때 주의할 점은 큐의 사이즈를 기준으로 반복문을 돌릴 경우 큐의 데이터가 poll되어 사이즈의 변동이 발생하기 때문에 별도의 리스트를 할당하여 데이터를 임시로 추가한 후 모든 로직이 완료된 후 해당 데이터를 추가하면 됩니다.

또한 이 문제의 가장 큰 주의점 중 하나는 입력 좌표를 굉장히 잘 봐야 한다는 것입니다. 문제에는 나무의 정보를 나타내는 세 정수 x, y, z가 주어진다. 라고 적혀있습니다. 하지만 만약 입력을 x,y,z 로 받고 이차원 배열을 A[세로][가로] 로 설정할 경우 오류가 발생합니다. 이는 문제에서 말하는 나무의 위치 (x, y) 값이 각각의 칸은 (r, c)로 나타내며, r은 가장 위에서부터 떨어진 칸의 개수, c는 가장 왼쪽으로부터 떨어진 칸의 개수 에 대응한다고 보면 x가 세로, y가 가로 라는 이상한 방식으로 입력을 받아야 합니다. 이 부분을 반드시 주의해서 푸셔야 합니다. (이 부분 때문에 맞는 정답이 계속 틀려서 원인을 한참동안 찾았습니다..)

다음은 코드입니다.

import java.util.*;
import java.io.*;

public class Main{

    static int N,M,K;
    static int res = 0;
    static int[] dx = {-1,0,1,1,1,0,-1,-1};
    static int[] dy = {-1,-1,-1,0,1,1,1,0};
    static int[][] addFood = new int[11][11];
    static int[][] farm = new int[11][11];
    // 나무 나이 정렬을 위해서 Priority Queue 사용
    static PriorityQueue<Tree> trees =  new PriorityQueue<>();
    static PriorityQueue<Tree> deadTrees =  new PriorityQueue<>();

    // 봄
    // 나무가 자신의 나이만큼 양분을 먹고 나이가 1 증가
    static void spring(){
        // 큐에서 poll할 시 데이터가 큐에서 빠지므로 빠진 데이터를 모아놓을 리스트 할당
        PriorityQueue<Tree> list = new PriorityQueue<>();

        while(!trees.isEmpty()){
            Tree tree = trees.poll();
            int y = tree.y;
            int x = tree.x;
            int age = tree.age;

            // 땅에 자신이 먹을 수 있는 양분이 있다면 자신의 나이만큼 양분 먹음
            if(farm[y][x] >= age){
                farm[y][x] -= age;
                // 나무가 살아있으므로 나이 1 증가시켜서 임시 리스트에 추가
                list.add(new Tree(x,y,age+1));
            }
            // 만약 땅에 양분이 부족하다면 나무는 죽음
            else deadTrees.add(tree);
        }

        // 임시 리스트에 저장된 데이터 trees에 저장
        trees = new PriorityQueue<>(list);
    }

    // 여름
    // 봄에 죽은 나무가 양분으로 변함
    static void summer(){
        while(!deadTrees.isEmpty()){
            Tree deadTree = deadTrees.poll();
            // 죽은 나무의 나이를 2로 나눈 값(소수점 생략)이 양분으로 추가
            int food = deadTree.age/2;
            int y = deadTree.y;
            int x = deadTree.x;
            farm[y][x] += food;
        }
    }

    // 가을
    // 나무 번식
    static void fall(){
        // 큐에서 poll할 시 데이터가 큐에서 빠지므로 빠진 데이터를 모아놓을 리스트 할당
        PriorityQueue<Tree> list = new PriorityQueue<>();

        while(!trees.isEmpty()){
            Tree tree = trees.poll();
            int y = tree.y;
            int x = tree.x;
            int age = tree.age;

            // 나이가 5의 배수인 나무가 번식
            if(age%5 == 0){
                // 인접한 8개의 칸에 나이가 1인 나무 생성
                for(int dir=0;dir<8;dir++){
                    int ny = y + dy[dir];
                    int nx = x + dx[dir];
                    if(ny<0 || ny>=N || nx<0 || nx>=N) continue;
                    // 임시 리스트에 나무 데이터 추가
                    list.add(new Tree(nx,ny,1));
                }
            }
            // 임시 리스트에 나무 데이터 추가
            list.add(tree);
        }

        // 임시 리스트에 저장된 데이터 trees에 저장
        trees = new PriorityQueue<>(list);
    }

    // 겨울
    // 양분 추가
    static void winter(){
        for(int i=0;i<N;i++){
            for(int j=0;j<N;j++){
                farm[i][j] += addFood[i][j];
            }
        }
    }

    static void doInvestment(int year){
        if(year == K){
            res = trees.size();
            return;
        }

        spring();
        summer();
        fall();
        winter();

        doInvestment(year+1);
    }

    public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        K = Integer.parseInt(st.nextToken());

        // 각 칸에 추가될 양분 데이터
        for(int i=0;i<N;i++){
            st = new StringTokenizer(br.readLine());
            for(int j=0;j<N;j++){
                addFood[i][j] = Integer.parseInt(st.nextToken());
                // 처음 양분의 양 : 5
                farm[i][j] = 5;
            }
        }

        // 나무 데이터
        for(int i=0;i<M;i++){
            st = new StringTokenizer(br.readLine());
            int y = Integer.parseInt(st.nextToken());
            int x = Integer.parseInt(st.nextToken());
            int z = Integer.parseInt(st.nextToken());
            // 1부터 시작하므로 좌표값 -1씩 진행
            trees.add(new Tree(x-1,y-1,z));
        }

        // K년이 지난 후 살아있는 나무의 개수이므로 0부터 시작
        doInvestment(0);
        System.out.println(res);
    }
}

// 나이가 적은 순으로 양분을 먹으므로 나이 순 오름차순으로 데이터 추가 필요
class Tree implements Comparable{
    int x;
    int y;
    int age;

    Tree(int x, int y, int z){
        this.x = x;
        this.y = y;
        this.age = z;
    }

    @Override
    public int compareTo(Object o) {
        Tree tree = (Tree) o;
        if(tree.age < age) return 1;
        else if(tree.age > age) return -1;
        else return 0;
    }
}

profile
저는 상황에 맞는 최적의 솔루션을 깊고 정확한 개념의 이해를 통한 다양한 방식으로 해결해오면서 지난 3년 동안 신규 서비스를 20만 회원 서비스로 성장시킨 Software Developer 최민길입니다.

0개의 댓글