본문 바로가기
프론트엔드로 가는 길/프로그래머스

[PCCP 기출문제] 1번 / 붕대 감기

by woody-j 2023. 12. 6.

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

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

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

붕대 감기는 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
function solution(bandage, health, attacks) {
    let currentHealth = health; // 현재 체력
    let healCount = 0; // 연속 성공 횟수
    let attacksTime = attacks[attacks.length - 1][0]; // 마지막 공격 시간

    for (let currentTime = 0; currentTime <= attacksTime; currentTime++) {
        // 체력이 최대 체력과 같거나 클 경우 연속 성공 횟수 증가
        if (currentHealth === health) {
            healCount++;
        }

        // 공격을 당하면 기술 취소, 연속 성공 횟수 초기화 및 체력 감소
        if (attacks[0][0] === currentTime) {
            healCount = 0;
            currentHealth -= attacks[0][1];

            // 체력이 0 이하인 경우 -1 반환
            if (currentHealth < 0) {
                return -1;
            }

            // 모든 공격이 끝난 후 현재 체력 반환
            if (attacksTime === currentTime) {
                return currentHealth;
            }

            // 다음 공격을 위해 배열에서 해당 공격 제거
            attacks.shift();
        }

        // 초당 회복량만큼 체력 회복
        currentHealth += bandage[1];

        // 연속 성공 횟수가 시전 시간과 같아지면 추가 회복량을 더해주고 성공 횟수 초기화
        if (bandage[0] === healCount) {
            currentHealth += bandage[2];
            healCount = 0;
        }
    }
}

 

문제를 풀지 못했다.

제 풀이에 대한 원인을 분석하고자했다.

 

1. let currentTime = 0;

currentTime가 1부터 시작하는 이유는 게임 시뮬레이션을 시작할 때, 초당 회복이나 몬스터의 공격은 0초가 아니라 1초부터 적용되기 때문입니다. 일반적으로 게임에서 시간은 1초부터 시작하며, 이에 따라 currentTime를 1부터 증가시켜가면서 시뮬레이션을 진행합니다.

따라서, currentTime가 1부터 시작하여 현재 시간을 나타내며, 각 초마다 회복 및 몬스터 공격이 발생하는 것을 시뮬레이션하는 방식으로 구현되었습니다.

 

2. 체력이 최대일 때 값을 가산 되는 것을 막는 것이 아니라 더해주되, 최대 체력으로 다시 값을 수정 함

// 현재 체력과 최대 체력이 동일 할 때, 체력은 증가 안시키고 연속 기술 성공 횟수만 증가시킴  
 if (currentHealth === health) {
            healCount++;
        }

 

이런식으로 최대,최소가 제한 되어있을 땐 Math.max/min 유용

health = Math.min(currentHealth, health);

 

3. 공격이 실행되면 실행된 공격은 attacks배열에서 사라지도록 했다.

 if (attacks[0][0] === currentTime) {
            healCount = 0;
            currentHealth -= attacks[0][1];

            // 체력이 0 이하인 경우 -1 반환
            if (currentHealth < 0) {
                return -1;
            }

            // 모든 공격이 끝난 후 현재 체력 반환
            if (attacksTime === currentTime) {
                return currentHealth;
            }

            // 다음 공격을 위해 배열에서 해당 공격 제거
            attacks.shift();
        }
  • 여기서 고려 할 점은 shift()가 매우 비효율적이라는 것.
  • 그리고 굳이 마지막 공격 시간과 현재 시간이 일치했을 때를 맞출 필요 없다,
    나는 attacks 배열을 앞에서 삭제시켰기에 마지막 attacks길이와 현재 시간을 맞춰 체력을 반환해줬다.
    모든 공격이 끝났을 때, 값을 반환 하면 된다.
            if (attacksTime === currentTime) {
                return currentHealth;
            }

이런식으로 수정

//++
let attackIndex = 0;  // 현재 몬스터 공격을 가리키는 인덱스

// 1부터 시작하여 마지막 몬스터 공격까지 반복
    for (let currentTime = 1; currentTime <= attacksTime; currentTime++) {
        // 현재 시간에 몬스터 공격이 있는지 확인
        if (attacks[attackIndex][0] === currentTime) {
            // 몬스터 공격
            체력 -= attacks[attackIndex][1];  // 캐릭터 체력에 피해를 적용
            if (currentHealth <= 0) return -1;  // 체력이 0 이하라면 캐릭터 사망
            attackIndex++;  // 다음 몬스터 공격으로 이동
            healCount = 0;  // 연속된 회복 카운터 초기화
        }
        .
        .
        .
}
//for문이 끝이 나고
    return currentHealth;  // 모든 공격을 처리한 후 남은 체력 반환
}

 

 

개선 코드 (for문)

function solution(bandage, health, attacks) {
    let attackIndex = 0;  // 현재 몬스터 공격을 가리키는 인덱스
    let healCount = 0;  // 연속된 회복이 적용되는 초의 카운터
      let currentHealth = health; // 현재 체력
    let attacksTime = attacks[attacks.length - 1][0]; // 마지막 공격 시간

    // 1부터 시작하여 마지막 몬스터 공격까지 반복
    for (let currentTime = 1; currentTime <= attacksTime; currentTime++) {
        // 현재 시간에 몬스터 공격이 있는지 확인
        if (attacks[attackIndex][0] === currentTime) {
            // 몬스터 공격
            currentHealth -= attacks[attackIndex][1];  // 캐릭터 체력에 피해를 적용
            if (currentHealth <= 0) return -1;  // 체력이 0 이하라면 캐릭터 사망
            attackIndex++;  // 다음 몬스터 공격으로 이동
            healCount = 0;  // 연속된 회복 카운터 초기화
        } else {
            // 회복 과정
            currentHealth += bandage[1];  // 초당 기본 회복 적용
            healCount++;

            // 추가 회복을 적용할 시간인지 확인
            if (healCount === bandage[0]) {
                currentHealth += bandage[2];  // 추가 회복 적용
                healCount = 0;  // 연속된 회복 카운터 초기화
            }

            currentHealth = Math.min(health, currentHealth);  // 현재 체력이 최대 체력을 초과하지 않도록 보정
        }
    }

    return currentHealth;  // 모든 공격을 처리한 후 남은 체력 반환
}

 

개선 코드 (while문)

function solution(bandage, health, attacks) {
    let currentHealth = health; // 현재 체력
    let successCount = 0; // 연속 성공 횟수
    let attackIndex = 0; // 현재 몬스터 공격을 가리키는 인덱스

    while (attackIndex < attacks.length) {
        // 몬스터 공격 처리
        if (attacks[attackIndex][0] === currentTime) {
            successCount = 0; // 연속 성공 횟수 초기화
            currentHealth -= attacks[attackIndex][1]; // 몬스터의 공격에 따른 체력 감소
            if (currentHealth <= 0) return -1; // 체력이 0 이하인 경우 -1 반환 (캐릭터 사망)
            attackIndex++; // 다음 몬스터 공격으로 이동
            continue; // 몬스터 공격이 있으면 아래의 회복 로직을 건너뛰고 다음 반복으로 진행
        }

        // 초당 회복량만큼 체력 회복
        currentHealth += bandage[1];

        // 연속 성공 횟수가 시전 시간과 같아지면 추가 회복량을 더해주고 성공 횟수 초기화
        if (bandage[0] === successCount) {
            currentHealth += bandage[2];
            successCount = 0;
        }

        // 현재 체력이 최대 체력을 초과하지 않도록 보정
        currentHealth = Math.min(health, currentHealth);
    }

    return currentHealth; // 모든 공격을 처리한 후 남은 체력 반환
}

부족한 점:

if 문이 끝나도 다른 코드는 실행이된다. 하지만 나는 그것을 인지하지 못 했다.

실행 되면 안되는 코드가 있었음에도 불구하고, if문 밖을 정리 하지 못했다.

if문의 조건을 확실하게 구분 지어 줘야겠다.

 

- for문의 시작 index가 무엇인지 잘 판단

- 최대,최소가 제한 되어있을 땐 Math.max/min 

- 어떤 배열이 주어지고 그 배열의 값을 다 활용한 후, 마지막에 값 반환

 

 

'프론트엔드로 가는 길 > 프로그래머스' 카테고리의 다른 글

최댓값과 최솟값  (0) 2023.12.04
Hash : 의상  (1) 2023.11.22
61. 여행경로  (0) 2023.09.17
60. H-Index  (0) 2023.09.17
59. k진수에서 소수 개수 구하기 - 2022 KAKAO BLIND RECRUITMENT  (0) 2023.09.14