알고리즘/프로그래머스 2단계

프로그래머스 - 디펜스 게임 - C++

게임만드는학생 2024. 9. 23. 14:22

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

 

프로그래머스

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

programmers.co.kr

 

#include <string>
#include <vector>
#include <queue>
using namespace std;

int solution(int n, int k, vector<int> enemy) {
    int answer = 0;
    priority_queue<int, vector<int>,greater<int>> pq;
    int total_damage=0;
    
    for(int i=0;i<enemy.size();i++)
    {
        pq.push(enemy[i]);
        
        if(pq.size()>k)
        {
            total_damage+=pq.top();
            pq.pop();
        }
        
        if(total_damage>n)
            return i;
    }
    
    return enemy.size();
}. 

 주어진 몬스터의 공격을 최대한 많이 막아낼 때, 몇라운드까지 막을 수 있는가가 문제이다. 

 

언제 방어권을 사용할 것인가? 가 가장 중요한 문제인데, 

처음엔 가장 큰 수에 대해서 방어하면 되겠다 생각했지만 방어권을 사용할 기회가 오기전에 게임이 종료될수도 있으니 이 또한 계속 따져야한다. 이를 for문을 돌며 하나씩 판단하려하니 머리가 너무 복잡해졌다.

 

하지만 우선순위큐(최소힙)을 사용하면 아주 간단하게 해결할 수 있다. 

 

우선순위큐에는 방어권을 사용할 라운드의 몬스터 수가 들어가게 된다. 

그러면 아래와 같이 문제를 풀어갈 수 있다.

 

1. 우선순위큐의 크기 > 방어권 수 이면 

가장 작은 수를 방어권 대신 직접 받아낸다. 

 

2. 받아낸 몬스터 수보다 기존 체력인 n 이 더 작다면 게임을 종료한다.

 

최소힙이기 때문에 꺼낼때는 O(1)의 시간이 걸리며 삽입시에도 O(log n) 시간이 걸리기 때문에 효율적이라 할 수 있다. 

 

#include <string>
#include <vector>
#include <queue>
using namespace std;

int solution(int n, int k, vector<int> enemy) {
    int answer = 0;
    priority_queue<int, vector<int>,greater<int>> pq;
    int total_damage=0;
    
    for(int i=0;i<enemy.size();i++)
    {
        pq.push(enemy[i]);
        
        if(pq.size()>k)
        {
            total_damage+=pq.top();
            pq.pop();
        }
        
        if(total_damage>n)
            return i;
    }
    
    return enemy.size();
}

위의 알고리즘을 코드로 옮긴 결과이다. 

먼저 큐에 데이터를 넣고 만약 방어권 수보다 큐의 크기가 커졌다면 그만큼 공격을 직접 받아내야한다. 

이 때, 최선의 선택은 가장 데미지가 약한 라운드를 받아내는 것이다. 

따라서 최소힙에서 top을 꺼내 데미지를 받는다. 

 

그 후, 체력이 남아 있다면 반복문을 진행한다. 

 

위와 같이 그때그때의 최선의 선택을 통해, 최적의 결론을 이끌어내는 방식이 그리디 알고리즘이다. 

그리디 알고리즘을 큐로도 구현가능하다는 점을 인지해두어야겠다.