본문 바로가기
Algorithm/Programmers

[Programmers] Lv.1 | [PCCP 기출문제] 1번 / 붕대 감기 | Java

by unknownomad 2025. 11. 10.

문제

https://school.programmers.co.kr/learn/courses/30/lessons/250137

 

문제 설명

어떤 게임에는 붕대 감기라는 기술이 있습니다.

붕대 감기t초 동안 붕대를 감으면서 1초마다 x만큼의 체력을 회복합니다. t초 연속으로 붕대를 감는 데 성공한다면 y만큼의 체력을 추가로 회복합니다. 게임 캐릭터에는 최대 체력이 존재해 현재 체력이 최대 체력보다 커지는 것은 불가능합니다.

기술을 쓰는 도중 몬스터에게 공격을 당하면 기술이 취소되고, 공격을 당하는 순간에는 체력을 회복할 수 없습니다. 몬스터에게 공격당해 기술이 취소당하거나 기술이 끝나면 그 즉시 붕대 감기를 다시 사용하며, 연속 성공 시간이 0으로 초기화됩니다.

몬스터의 공격을 받으면 정해진 피해량만큼 현재 체력이 줄어듭니다. 이때, 현재 체력이 0 이하가 되면 캐릭터가 죽으며 더 이상 체력을 회복할 수 없습니다.

당신은 붕대감기 기술의 정보, 캐릭터가 가진 최대 체력과 몬스터의 공격 패턴이 주어질 때 캐릭터가 끝까지 생존할 수 있는지 궁금합니다.

붕대 감기 기술의 시전 시간, 1초당 회복량, 추가 회복량을 담은 1차원 정수 배열 bandage와 최대 체력을 의미하는 정수 health, 몬스터의 공격 시간과 피해량을 담은 2차원 정수 배열 attacks가 매개변수로 주어집니다. 모든 공격이 끝난 직후 남은 체력을 return 하도록 solution 함수를 완성해 주세요. 만약 몬스터의 공격을 받고 캐릭터의 체력이 0 이하가 되어 죽는다면 -1을 return 해주세요.


제한사항

bandage는 [시전 시간, 초당 회복량, 추가 회복량] 형태의 길이가 3인 정수 배열입니다.

  • 1 ≤ 시전 시간 = t ≤ 50
  • 1 ≤ 초당 회복량 = x ≤ 100
  • 1 ≤ 추가 회복량 = y ≤ 100

1 ≤ health ≤ 1,000

1 ≤ attacks의 길이 ≤ 100

  • attacks[i]는 [공격 시간, 피해량] 형태의 길이가 2인 정수 배열입니다.
  • attacks공격 시간을 기준으로 오름차순 정렬된 상태입니다.
  • attacks공격 시간은 모두 다릅니다.
  • 1 ≤ 공격 시간 ≤ 1,000
  • 1 ≤ 피해량 ≤ 100

입출력 예

bandage health attacks result
[5, 1, 5] 30 [[2, 10], [9, 15], [10, 5], [11, 5]] 5
[3, 2, 7] 20 [[1, 15], [5, 16], [8, 6]] -1
[4, 2, 7] 20 [[1, 15], [5, 16], [8, 6]] -1
[1, 1, 1] 5 [[1, 2], [3, 2]] 3

입출력 예 설명

입출력 예 #1

몬스터의 마지막 공격은 11초에 이루어집니다. 0초부터 11초까지 캐릭터의 상태는 아래 표와 같습니다.

 

시간 현재 체력(변화량) 연속 성공 공격 설명
0 30 0 X 초기 상태
1 30(+0) 1 X 최대 체력 이상의 체력을 가질 수 없습니다.
2 20(-10) 0 O 몬스터의 공격으로 연속 성공이 초기화됩니다.
3 21(+1) 1 X  
4 22(+1) 2 X  
5 23(+1) 3 X  
6 24(+1) 4 X  
7 30(+6) 5 → 0 X 5초 연속 성공해 체력을 5만큼 추가 회복하고 연속 성공이 초기화됩니다.
8 30(+0) 1 X 최대 체력 이상의 체력을 가질 수 없습니다.
9 15(-15) 0 O 몬스터의 공격으로 연속 성공이 초기화됩니다.
10 10(-5) 0 O 몬스터의 공격으로 연속 성공이 초기화됩니다.
11 5(-5) 0 O 몬스터의 마지막 공격입니다.

몬스터의 마지막 공격 직후 캐릭터의 체력은 5입니다. 따라서 5을 return 해야 합니다.

 

입출력 예 #2

몬스터의 마지막 공격은 8초에 이루어집니다. 0초부터 8초까지 캐릭터의 상태는 아래 표와 같습니다.

 

시간 현재 체력(변화량) 연속 성공 공격 설명
0 20 0 X 초기 상태
1 5(-15) 0 O 몬스터의 공격으로 연속 성공이 초기화됩니다.
2 7(+2) 1 X  
3 9(+2) 2 X  
4 18(+9) 3 → 0 X 3초 연속 성공해 체력을 7만큼 추가 회복하고 연속 성공이 초기화됩니다.
5 2(-16) 0 O 몬스터의 공격으로 연속 성공이 초기화됩니다.
6 4(+2) 1 X  
7 6(+2) 2 X  
8 0(-6) 0 O 몬스터의 마지막 공격을 받아 캐릭터의 체력이 0 이하가 됩니다.

몬스터의 공격을 받아 캐릭터의 체력이 0 이하가 됩니다. 따라서 -1을 return 해야 합니다.

 

입출력 예 #3

몬스터의 마지막 공격은 8초에 이루어집니다. 0초부터 5초까지 캐릭터의 상태는 아래 표와 같습니다.

 

시간 현재 체력(변화량) 연속 성공 공격 설명
0 20 0 X 초기 상태
1 5(-15) 0 O 몬스터의 공격으로 연속 성공이 초기화됩니다.
2 7(+2) 1 X  
3 9(+2) 2 X  
4 11(+2) 3 X  
5 -5(-16) 0 O 몬스터의 공격을 받아 캐릭터의 체력이 0 이하가 됩니다.

몬스터의 공격을 받아 캐릭터의 체력이 0 이하가 됩니다. 따라서 -1을 return 해야 합니다.

 

입출력 예 #4

몬스터의 마지막 공격은 3초에 이루어집니다. 0초부터 3초까지 캐릭터의 상태는 아래 표와 같습니다.

 

시간 현재 체력(변화량) 연속 성공 공격 설명
0 5 0 X 초기 상태
1 3(-2) 0 O 몬스터의 공격으로 연속 성공이 초기화됩니다.
2 5(+2) 1 → 0 X 1초 연속 성공해 체력을 1만큼 추가 회복하고 연속 성공이 초기화됩니다.
3 3(-2) 0 O 몬스터의 마지막 공격입니다.

몬스터의 마지막 공격 직후 캐릭터의 체력은 3입니다. 따라서 3을 return 해야 합니다.


풀이

class Solution {
    public int solution(int[] bandage, int health, int[][] attacks) {
        // 붕대 감기 기술 정보 꺼내기
        int t = bandage[0]; // 연속 성공 기준 시간
        int x = bandage[1]; // 초당 회복량
        int y = bandage[2]; // t초 연속 성공 시 추가 회복량

        int maxHealth = health; // 최대 체력 저장 (회복 시 상한선)
        int currentHealth = health; // 현재 체력 (매초 변할 값)
        int successTime = 0; // 현재 연속으로 붕대를 감은 시간 (공격 받으면 0초로 초기화)

        // 공격 시간 탐색을 위한 인덱스
        int attackIndex = 0;
        int lastAttackTime = attacks[attacks.length - 1][0]; // 마지막 공격 시간

        // 1초부터 마지막 공격 시간까지 시뮬레이션
        for (int time = 1; time <= lastAttackTime; time++) {

            // ① 이번 초가 공격 시간인지 확인
            if (attackIndex < attacks.length && attacks[attackIndex][0] == time) {
                // 공격을 받는 순간 → 회복 불가능 + 체력 감소
                currentHealth -= attacks[attackIndex][1]; // 공격 피해 반영
                successTime = 0; // 연속 붕대 성공 시간 초기화
                attackIndex++; // 다음 공격으로 인덱스 이동

                // 공격받고 죽었는지 확인
                if (currentHealth <= 0) {
                    return -1; // 즉시 사망 → 종료
                }

                continue; // 공격받은 초에는 회복하지 않으므로 다음 초로
            }

            // ② 공격이 없는 평화로운 초 → 회복 가능
            successTime++; // 연속 붕대 시간 +1초

            // 초당 회복량 x만큼 회복
            currentHealth += x;

            // 만약 연속 성공 시간이 t초면 추가 회복 y 발동
            if (successTime == t) {
                currentHealth += y;
                successTime = 0; // 연속 성공 보너스 후 다시 초기화
            }

            // 체력은 최대 체력을 넘길 수 없음
            if (currentHealth > maxHealth) {
                currentHealth = maxHealth;
            }
        }

        // 마지막 공격까지 생존했다면 남은 체력 반환
        return currentHealth;
    }
}
import java.util.*;

class Solution {
    public int solution(int[] bandage, int health, int[][] attacks) {

        // 붕대 감기 스킬 정보
        int t = bandage[0]; // 연속으로 붕대를 감는 시간 (t초마다 보너스 회복 발생)
        int x = bandage[1]; // 초당 회복량
        int y = bandage[2]; // t초 연속 성공 시 추가 회복량

        int maxHealth = health;   // 캐릭터의 최대 체력 (넘으면 안 됨)
        int currentHealth = health; // 현재 체력 (계속 변함)
        int lastAttackTime = 0;   // 마지막으로 공격을 받은 시간 (초 단위)

        // 핵심 아이디어:
        // 공격이 많아도 초마다 시뮬레이션하지 않고,
        // "이전 공격 이후 ~ 이번 공격 전까지"의 회복량만 수식으로 한 번에 계산함
        // 즉, 공격 간의 간격(peacefulSeconds)만 이용해서 체력 변화를 계산함

        for (int[] attack : attacks) {
            // =============================================
            // 0. 만약 이전에 이미 죽은 상태라면 더 계산할 필요 없음
            // =============================================
            if (currentHealth <= 0) {
                return -1;
            }

            // =============================================
            // 1. 공격 전까지의 '평화로운 시간' 계산
            // =============================================
            // 예: 직전 공격이 2초, 이번 공격이 9초라면
            //     공격 없는 시간은 (9 - 2 - 1) = 6초
            // '-1'을 하는 이유:
            // 공격받은 초에는 회복이 불가능하기 때문
            int peacefulSeconds = attack[0] - lastAttackTime - 1;

            // =============================================
            // 2. 그 시간 동안 초당 회복량 적용
            // =============================================
            // 1초마다 x만큼 회복하므로 총 회복량 = peacefulSeconds * x
            int healFromSeconds = peacefulSeconds * x;

            // =============================================
            // 3. 그 시간 동안 't초 연속 성공 보너스' 횟수 계산
            // =============================================
            // peacefulSeconds 중 t초 단위로 몇 번 완성했는지 확인
            // 예: t=5, peacefulSeconds=12라면 → 12 / 5 = 2회 보너스 발생
            int successCount = peacefulSeconds / t;

            // 추가 회복량은 (보너스 횟수 * y)
            int healFromBonus = successCount * y;

            // =============================================
            // 4. 회복 총합 반영 (단, 최대 체력 초과 불가)
            // =============================================
            currentHealth += healFromSeconds + healFromBonus;

            if (currentHealth > maxHealth) {
                currentHealth = maxHealth;
            }

            // =============================================
            // 5. 공격 피해 적용
            // =============================================
            // 공격이 들어오는 초에는 회복이 불가능하므로
            // 지금 시점에서 체력을 바로 감소시켜야 함
            currentHealth -= attack[1];

            // =============================================
            // 6. 공격 직후 즉시 사망 판정
            // =============================================
            // 공격받고 나서 체력이 0 이하가 되면 게임 종료
            // 다음 공격까지 계산할 필요 없음.
            if (currentHealth <= 0) {
                return -1;
            }

            // =============================================
            // 7. 이번 공격을 기준으로 다음 공격 간격 계산을 위해 시점 저장
            // =============================================
            // 다음 공격 때 '공격 사이 회복 시간'을 구하기 위해
            // 현재 공격 시간을 마지막 공격 시점으로 저장해 둠
            lastAttackTime = attack[0];
        }

        // =============================================
        // 8. 모든 공격이 끝난 뒤 남은 체력 반환
        // =============================================
        // 여기까지 도달했다는 건 끝까지 생존했다는 의미
        return currentHealth;
    }
}

댓글