DEVELOP
article thumbnail

붕대 감기 #210137

더보기

문제 설명

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

붕대 감기는 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

입출력 예bandagehealthattacksresult
[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 해야 합니다.

나의 코드

public class Solution {
    public int solution(int[] bandage, int health, int[][] attacks) {
        int MAX_health = health;
        int success = 0;
        int sec = 0;
        int attack_idx = 0;

        while (true) {
            sec++;
            if (sec == attacks[attack_idx][0]) {
                health -= attacks[attack_idx][1];
                attack_idx++;
                success = 0;
                // 공격 후 체력 감소
            } else {
                // 붕대 감기
                success++;
                if (health <= MAX_health - bandage[1]) {
                    health += bandage[1];
                } else {
                    health = MAX_health;
                }
                if (success == bandage[0]) {
                    // 연속 붕대 성공
                    success = 0;
                    if (health < MAX_health) {
                        health += bandage[2];
                    }
                }
            }

            if (health <= 0) return -1;
            if (attack_idx == attacks.length) break;
        }

        return health;
    }
}

처음에 풀 때는 틀렸었다.

틀린 이유는 아래 코드 부분으로 체력이 최댓값 이상으로 증가될 때를 고려하지 못했다.

                if (health <= MAX_health - bandage[1]) {
                    health += bandage[1];
                } else {
                    health = MAX_health;
                }

단순히, 추가 회복량만큼을 더했을 떄, 최대치 보다 크면 안더하기 << 라고 생각을 했는데,

질문하기에 있는 글 몇개를 보니까, 최대치까지만 증가 << 이 개념이었다.

프로그래머스는 가끔 저런 말장난 같은,, 알아보기 어려운 것들이 간혹  있는 것 같다. (개인생각)

 

  • 처음 health값(최댓값)을 MAX_health에 저장한다. 
  • 붕대감기 성공횟수를 파악할 success 변수를 0으로 초기화한다. 
  • 현재 초를 의미하는 sec 변수를 0으로 초기화한다.
  • 공격을 받은 것이 몇번째인지 파악하기 위해 attack_idx을 0으로 초기화한다.
  • while 무한반복문을 도는데, 
    • 루프가 한번 도는 것을 1초 증가로 생각하고 sec를 1 증가시킨다.
    • 현재 시각이  공격 배열의 시각과 같으면, 체력을 감소시키고, attack_idx를 1 증가시킨뒤, 연속 성공 횟수를 0으로 초기화한다.
    • 그렇지 않으면, 공격 받지 않았을 때이므로 연속 성공횟수를 1증가시키고, 위에서 설명한 것처럼 Max보다 커지지 않게 체력을 증가시킨다. 
    • 만약 연속 성공횟수가 주어진 수와 같게 되면, 다시 연속 성공 횟수를 0으로 초기화하고, 최댓값보다 작을 때만 추가회복량만큼 증가시킨다. ( 지금 보니까 여기도 위에 처럼 딱 최댓값까지만 증가하도록 하는 코드가 있어야 맞는 거 같긴 하다.)

다른 풀이

    public int solution(int[] bandage, int health, int[][] attacks) {
        int MAX_health = health; // 최대 체력
        int last = 0; // 가장 마지막으로 공격을 받은 시각

        int stk_cnt, bonus_cnt; // 기술 성공 횟수, 보너스 받을 횟수
        for (int[] atk : attacks) {
            if (health <= 0) {
                return -1;
            }

            stk_cnt = atk[0] - last - 1; // 마지막 공격 시각과 현재 시각의 차이 
            bonus_cnt = stk_cnt / bandage[0]; // 보너스 받는 횟수 

            health = Math.min(MAX_health, health + stk_cnt * bandage[1]); // 붕대 성공횟수만큼 증가, MAX보다 커지먄, MAX를 health로,
            health = Math.min(MAX_health, health + bonus_cnt * bandage[2]); // 보너스횟수만큼 증가, MAX보다 커지먄, MAX를 health로,

            health -= atk[1]; // 공격 데미지 
            last = atk[0]; // 마지막 공격 시각 업데이트 
        }

        return health <= 0 ? -1 : health;
    }
  • sec를 1초씩 증가시키는 것이 아닌, 가장 마지막으로 공격 받은 시각을 저장하고, 그 값을 기준으로 체력을 증가/감소 시킨다.
  • 특히나, Math.min을 써서, MAX_health 값보다 커지지 않게 조절하는 코드가 좋았다. 
  • 무한루프를 돌지 않고 위와 같이, 공격 배열의 길이만큼만 수행이 되니 더욱 좋은 것 같다. 

나의 풀이와 다른 풀이의 비교

내 풀이보다 다른 풀이가 시간이 더 짧게 걸린다. 

profile

DEVELOP

@JUNGY00N