알고리즘 문제풀이/SWEA

[SWEA/Java] 5653. 줄기세포배양

이제ise이제 2022. 9. 16. 19:32

[순서]

  • [구상]
  • [성공코드]
  • [실패코드] 

 

[구상]

1. 입력받는다. (세포가 번식하며 확장할 것이므로 0, 0부터 시작X 가운데부터O. 맵 크기는 엄청 크게)

2. 세포의 상태를 enum으로 나누기. (빈자리, 이제막 번식된 세포인지, 비활성화 상태, 활성화 상태, 죽은 세포)

3. K시간 동안 반복. (1부터 K까지)

4. 세포 상태에 따라 할 일이 달라짐. (비활성화 -> 활성화 타이머, 활성화->죽음 타이머, 활성화로부터 1시간 이후만 체크하는 타이머 필요 => timer 1개로 통일)

 


 

[성공코드]

아래 더보기 클릭

더보기
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Solution_5653_줄기세포배양 {

    static int[][] deltas = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } };

    static int N, M, K; // 세로크기, 가로크기, 배양 시간
    static int[][] map; // 전체 맵
    static int[][] timer; // 비활->활성화 카운트 & 활성->죽음 카운트 & 활성하고 1시간 지난 뒤에 번식할 타임체크
    static eStatus[][] status; // 세포의 상태

	// 빈 자리, 세포가 새로 생긴 자리인지, 비활성화, 활성화, 죽은 세포
    static enum eStatus {
        idle, newSepo, notActivate, activate, die
    };

    static class Sepo {
        int i, j, k; // 좌표 행, 열, 배양시간

        public Sepo(int i, int j, int k) {
            super();
            this.i = i;
            this.j = j;
            this.k = k;
        }
    }

    public static void main(String[] args) throws NumberFormatException, IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    int T = Integer.parseInt(br.readLine());

    for (int t = 1; t <= T; t++) {
        int res = 0; // 정답 변수: 살아있는 줄기세포 개수 = 비활성상태+활성상태

        // 0. 입력
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        K = Integer.parseInt(st.nextToken());

        // <POINT!> K 시간 후까지만 알면 되므로 K 만큼 추가
        // 양옆위아래를 생각해서 2*K로 하면 시간 초과 발생함
        // 어차피 비활->활성화 시간, 활성->번식 시간이 필요하므로 K면 충분
        map = new int[N + K][M + K];
        timer = new int[N + K][M + K];
        status = new eStatus[N + K][M + K];

        for (int i = 0; i < status.length; i++) {
            Arrays.fill(status[i], eStatus.idle);
        }

        // <POINT!> 입력은 크기만큼만 입력
        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < M; j++) {
                // 현재 존재하는 세포 위치
                int sepoI = i + map.length / 2 - N / 2;
                int sepoJ = j + map[i].length / 2 - M / 2;

                // <POINT!> 가운데부터 세포 세팅
                map[sepoI][sepoJ] = Integer.parseInt(st.nextToken());
                timer[sepoI][sepoJ] = map[sepoI][sepoJ];
                if (map[sepoI][sepoJ] != 0) {
                    status[sepoI][sepoJ] = eStatus.notActivate;
                    res++;
                }
            }
        } // ===== 입력 끝

        // [LOGIC]
        // <POINT!> k시간 후 .. K시간 후까지
        for (int k = 1; k <= K; k++) {
            // 1. 맵 돌기
            for (int i = 0; i < map.length; i++) {
                for (int j = 0; j < map[i].length; j++) {
                    int hp = map[i][j]; // 생명력 수치

                    switch (status[i][j]) {
                        case notActivate: // 활성화 아직 X
                            // activate 타이머 돌리기
                            timer[i][j]--;
                            if (timer[i][j] == 0) {
                                status[i][j] = eStatus.activate;
                                timer[i][j] = map[i][j]; // die 타이머 활성화
                            }
                        break;
                        case activate: // 활성화 O
                            // die 타이머 돌리기
                            timer[i][j]--;
                            // die 타이머 종료시 사망
                            if (timer[i][j] == 0) {
                                // 죽었으면 살아있는 줄기세포수 감소
                                status[i][j] = eStatus.die;
                                res--;
                            }
                            // 타이머가 1시간 돌았을 때 => 주변으로 번식
                            if (timer[i][j] == map[i][j] - 1) {
                                for (int d = 0; d < deltas.length; d++) {
                                    int ni = i + deltas[d][0];
                                    int nj = j + deltas[d][1];
                                    if (ni >= 0 && nj >= 0 && ni < map.length && nj < map[i].length) {
                                        // 빈 공간일 때 번식
                                        if (status[ni][nj] == eStatus.idle) {
                                            map[ni][nj] = hp;
                                            res++;
                                            status[ni][nj] = eStatus.newSepo;
                                        }
                                        // <POINT!> 이번 시간대에 새로 번식한 곳 => 근데 hp가 더 큰 값이 있으면 그걸로 번식
                                        else if (status[ni][nj] == eStatus.newSepo) {
                                            // 큰놈으로 덧씌움
                                            if (map[ni][nj] < hp) {
                                                map[ni][nj] = hp;
                                            }
                                        }
                                    }
                                }
                            }
                        break;
                    } // end of switch
                }
            }

            // 신입 세포 -> notActivate로 전환
            for (int i = 0; i < map.length; i++) {
                for (int j = 0; j < map[i].length; j++) {
                    if (status[i][j] == eStatus.newSepo) {
                        status[i][j] = eStatus.notActivate;
                        timer[i][j] = map[i][j]; // activate 타이머 활성화
                    }
                }
            }
        } // end of K timer
        System.out.println("#" + t + " " + res);
        } // end of test case
    }
}

 

 

맵 크기가 문제였다!

세포는 맵의 중앙부터 시작하고, K 시간동안 번식한다.

번식은 상하좌우로 가니까 양옆으로 번식할 거 고려해서 "맵크기 = 초기값+2*K 로 하면 되겠지?" 라고 생각한 게 안일했다.

이것 때문에 시간초과가 났다! 세포가 있는 맵 위치부터 안돌고 맵 전체를 전부 탐색하고 있으니 시간 초과 날 수 밖에.

 

막상 찍어보니까 2K만큼 번식하지 않은데.. 그냥 K로 해볼까? 라는 생각으로 줄여봤다.

어차피 비활->활성화 시간, 활성->번식 시간이 필요하므로 K면 충분했다.

        ///////////// AFTER
        map = new int[N + K][M + K];
        timer = new int[N + K][M + K];
        status = new eStatus[N + K][M + K];
        
        ///////////// BEFORE
        map = new int[N + K * 2][M + K * 2];
        timer = new int[N + K * 2][M + K * 2];
        status = new eStatus[N + K * 2][M + K * 2];

 

더보기

<1번 테스트 케이스>

1
5 5 19
3 2 0 3 0 
0 3 0 0 0 
0 0 0 0 0 
0 0 1 0 0 
0 0 0 0 2

1번 테스트 케이스

 

'1번 테스트케이스 K 시간 후 [After] / [Before] 

 

으악내눈!

 

잘 줄였다.

 


[실패코드]

 

아래 더보기 클릭

더보기
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Solution_5653_줄기세포배양 {

static int[][] deltas = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } };

static int N, M, K; // 세로크기, 가로크기, 배양 시간
static int[][] map;
static int[][] timer;
static eStatus[][] status;

// 초기세팅, 세포가 새로 생긴 자리인지, 비활성화, 활성화, 죽은 세포
static enum eStatus {
    idle, newSepo, notActivate, activate, die
};

public static void main(String[] args) throws NumberFormatException, IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    int T = Integer.parseInt(br.readLine());

    for (int t = 1; t <= T; t++) {
        int res = 0; // 살아있는 줄기세포 개수 = 비활성상태+활성상태

        // 0. 입력
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        K = Integer.parseInt(st.nextToken());

        // <POINT!> K 시간 후까지만 알면 되니까
        map = new int[N + K * 2][M + K * 2];
        timer = new int[N + K * 2][M + K * 2];
        status = new eStatus[N + K * 2][M + K * 2];

        for (int i = 0; i < status.length; i++) {
            for (int j = 0; j < status[i].length; j++) {
                status[i][j] = eStatus.idle;
            }
        }
        // <POINT!> 입력 크기만큼만 입력
        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < M; j++) {
                // 현재 존재하는 세포 위치
                int sepoI = i + map.length / 2 - 1;
                int sepoJ = j + map[i].length / 2 - 1;

                // <POINT!> 가운데부터 세포증식
                map[sepoI][sepoJ] = Integer.parseInt(st.nextToken());
                timer[sepoI][sepoJ] = map[sepoI][sepoJ];
                if (map[sepoI][sepoJ] != 0) {
                    status[sepoI][sepoJ] = eStatus.notActivate;
                    res++;
                }
            }
        }
        // ===== 입력 끝

//			// [DEBUG] 맵
//			for (int i = 0; i < map.length; i++) {
//				for (int j = 0; j < map[i].length; j++) {
//					System.out.print(map[i][j] + " ");
//				}
//				System.out.println();
//			}

        // [LOGIC]
        // <POINT!> k시간 후 .. K시간 후까지
        for (int k = 1; k <= K; k++) {
//				// [DEBUG] 시간체크
//				System.out.println(k + "시간 뒤:");
            // 맵 돌기
            for (int i = 0; i < map.length; i++) {
                for (int j = 0; j < map[i].length; j++) {
                    int hp = map[i][j]; // 생명력 수치

                    switch (status[i][j]) {
                    case notActivate: // 활성화 아직 X
                        // activate 타이머 돌리기
                        timer[i][j]--;
                        if (timer[i][j] == 0) {
                            status[i][j] = eStatus.activate;
                            timer[i][j] = map[i][j]; // die 타이머 활성화
                        }
                        break;
                    case activate: // 활성화 O
                        // die 타이머 돌리기
                        timer[i][j]--;
                        // die 타이머 종료시 사망
                        if (timer[i][j] == 0) {
                            // 죽었으면 살아있는 줄기세포수 감소
                            status[i][j] = eStatus.die;
                            res--;
                        }
                        // 활성화 O && 타이머가 1시간 돌았을 때 => 주변으로 번식
                        if (timer[i][j] == map[i][j] - 1) {
                            for (int d = 0; d < deltas.length; d++) {
                                int ni = i + deltas[d][0];
                                int nj = j + deltas[d][1];
                                if (ni >= 0 && nj >= 0 && ni < map.length && nj < map[i].length) {
                                    // 빈 공간일 때 번식
                                    if (status[ni][nj] == eStatus.idle) {
                                        map[ni][nj] = hp;
                                        res++;
                                        status[ni][nj] = eStatus.newSepo;
                                    }
                                    // <POINT!> 이번 시간대에 새로 번식한 곳 => 근데 hp가 더 큰 값이 있으면 그걸로 번식
                                    else if (status[ni][nj] == eStatus.newSepo) {
                                        // 큰놈으로 덧씌움
                                        if (map[ni][nj] < hp) {
                                            map[ni][nj] = hp;
                                        }
                                    }
                                }
                            }
                        }
                        break;
                    } // end of switch
                }
            }

            // 신입 세포 -> notActivate로 전환
            for (int i = 0; i < map.length; i++) {
                for (int j = 0; j < map[i].length; j++) {
                    if (status[i][j] == eStatus.newSepo) {
                        status[i][j] = eStatus.notActivate;
                        timer[i][j] = map[i][j]; // activate 타이머 활성화
                    }
                }
            }
//				// [DEBUG] 맵
//				for (int i = 0; i < map.length; i++) {
//					for (int j = 0; j < map[i].length; j++) {
//						int stat = 0;
//						switch (status[i][j]) {
//						case idle:
//							stat = 0;
//							break;
//						case newSepo:
//							stat = 1;
//							break;
//						case notActivate:
//							stat = 2;
//							break;
//						case activate:
//							stat = 3;
//							break;
//						case die:
//							stat = 4;
//							break;
//						}
//						System.out.print(map[i][j] + "(" + stat + ") ");
//					}
//					System.out.println();
//				} // end of [DEBUG]
        } // end of K timer
        System.out.println("#" + t + " " + res);
    } // end of test case
}
}