프로그래머스 - 체육복
https://programmers.co.kr/learn/courses/30/lessons/42862
문제 설명
점심 시간에 도둑이 들어 일부 학생이 체육복을 도난당했다.
다행히도 여벌 체육복이 있는 학생이 있어, 체육복을 빌려줄 수 있다.
하지만, 학생들의 번호는 체격 순으로 매겨져 있기 때문에, 바로 앞번호 혹은
뒷번호 학생에게만 체육복을 빌려줄 수 있다.
전체 학생수 n
명 중, 체육복을 도난 당한 학생들의 번호가 담긴 배열 lost
와
여벌을 가진 학생들의 번호가 담긴 배열 reserve
를 토대로,
체육복을 갖출 수 있는 학생의 최댓값을 리턴하자.
제한 사항
- 2 <= 전체 학생수 <= 30
- 1 <=
len(lost)
<= 전체 학생수 - 1 <=
len(reserve)
<= 전체 학생수 - 여벌 체육복이 있어야만 체육복을 빌려줄 수 있다.
- 여벌 체육복을 가진 학생도 도난을 당할 수 있다. 이렇게 되면 해당 학생은 남은 체육복이 1개이므로, 빌려줄 수 없다.
1. 탐색 - O(N)
최대한 많은 학생들이 체육복을 입을 수 있는 경우를 찾아야 한다. 그러기 위해서는, 각 학생마다 가지고 있는 체육복의 수를 파악할 필요가 있다.
다음 예제를 살펴보자.
n=8, lost=[2,4,6,7], reserve=[1,3,7,8]
점심 시간 이전의 학생들의 체육복 보유 현황은 다음과 같다.
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|
2 | 1 | 2 | 1 | 1 | 1 | 2 | 2 |
점심 시간에 도둑이 든 이후 학생들의 체육복 보유 현황은 다음과 같다.
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|
2 | 0 | 2 | 0 | 1 | 0 | 1 | 2 |
이렇게 되면, 지금 체육복을 가지고 있지 않은 학생은
2
, 4
그리고 6
번 학생이다.
그리고 여유 체육복을 가진 학생은 1
, 3
그리고 8
번이다.
2
번 학생은 누구에게 체육복을 빌릴 수 있을까?
1
번과 3
번에게 체육복을 받을 수 있다.
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|
2 | 1 | 1 | 0 | 1 | 0 | 1 | 2 |
만약 2
번 학생이 3
번 학생의 체육복을 빌렸다고 치자.
그러면 4
번 학생은 누구에게 체육복을 빌릴 수 있을까?
누구에게도 빌릴 수 없다.
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 | 0 | 1 | 2 |
하지만 2
번이 1
번 학생의 체육복을 빌린다면,
4
번 학생은 3
번 학생에게 체육복을 빌릴 수 있다.
위의 상황을 보아하니 누구의 체육복을 빌리느냐에 따라
체육복의 분배가 달라질 수 있다는 것을 알 수 있다.
여기서 6,7,8
번의 관계에 대해 주의해야 할 점이 있다.
7
번은 원래 여벌을 가지고 있던 학생이었으나, 체육복을 도둑맞아
단 하나의 체육복만 남은 상황이다.
그리고 자신의 옆번호인 6
번은 체육복을 가지고 있고,
또 다른 옆번호인 8
번은 여벌을 가지고 있는 상태다.
7
번이 6
번에게 체육복을 빌려주고,
7
번은 8
번의 체육복을 빌려서 6,7,8
번 모두가
체육수업을 들을 수 있으면 좋겠지만, 안타깝게도 이 문제에서는
이러한 동작은 허용하지 않는 것으로 보인다.
체육복이 2
벌 있을 때에만 옆친구에게 빌려줄 수 있다고 생각하자.
그러면 위의 예시에서 체육복을 입을 수 있는 학생은 6
번 빼고 모두이므로,
정답은 7
이 된다.
그러면 이 과정을 일반화를 해야 한다. 어떻게 해야 할까?
맨 왼쪽의 학생, 1
번부터 n
번까지 탐색을 해 보자.
그리고 현재 탐색중인 학생을 i
번이라고 하자.
만약 i
번 학생의 체육복이 1개 이상이라면 다음 학생으로 넘어가면 된다.
만약 i
번 학생의 체육복이 0개라면, 옆의 학생으로부터 체육복을 빌려야 한다.
이 때 누구의 체육복부터 확인하는 것이 좋을까?
지금까지 지나온 i-1
번째 학생의 체육복부터 확인하는 것이 좋을 것이다.
i
번 학생까지의 체육복 수를 검사하면서, i-1
번의 학생의 체육복이
여유가 남아있다는 것은, i-1
번 학생은 이제 i
번 학생 말고는
체육복을 빌려줄 사람이 없다는 것을 의미한다. 하지만 i+1
번 학생은
i
번 학생에게도 체육복을 빌려줄 수 있지만, 아직 확인해보지 않은
i+2
번 학생에게도 빌려줄 여지가 있다.
그러므로, i-1
번 학생의 여유 체육복을 먼저 확인해야 한다.
그러면 i-1
번 학생은 체육복이 없고, i+1
번 학생이 체육복이 있다면?
그 때는 i+1
번 학생의 체육복을 빌려도 된다.
이 경우에도 i+2
번 학생의 체육복이 없을 수도 있고, i+3
번 학생도
체육복이 없어서 i
번 학생이 i+1
번 학생의 체육복을 빌리면
i+2
번 학생은 체육복을 빌리지 못하게 될 것이다.
그렇다고 i
번 학생이 양보해서 i+2
번 학생이 체육복을 입으면?
어자피 i
번이나 i+2
번 중 한 명만이 입을 수 있는 경우다.
그리고 i+3
번 학생이 여벌이 있는 경우를 생각한다면,
그냥 i
번 학생이 빌려가는 편이 생각을 덜 해도 되서 간단해진다.
그러면 다음과 같은 과정을 거치면 될 것이다.
1
번 학생부터n
번 학생까지 체육복 수를 검사한다. 그리고 현재 탐색하는 학생의 번호를i
번이라고 한다.- 만약
i
번 학생의 체육복이 1개 이상이라면 그냥 넘어간다. - 만약
i
번 학생의 체육복이 0개라면, 다음과 같은 과정을 거친다.
i-1
번 학생에 여벌이 있다면 빌린다.- 만약
i-1
번에게서 빌리지 못했다면,i+1
번 학생에게 여벌이 있다면 빌린다.- 그리고 다시 모든 학생의 체육복 수를 검사하여, 1개 이상의 체육복을 가진 학생의 수를 리턴한다.
- 만약
그러면 위의 내용을 코드로 표현해보자.
solution.cpp
#include <vector>
using namespace std;
int solution(int n, vector<int> lost, vector<int> reserve) {
vector<int> suit(n+2,1);
suit[0] = 0; suit[n+1] = 0;
for (int i=0;i<lost.size();++i)
--suit[lost[i]];
for (int i=0;i<reserve.size();++i)
++suit[reserve[i]];
for (int i=1;i<=n;++i)
if (0 == suit[i])
if (suit[i-1] == 2)
suit[i-1] = suit[i] = 1;
else if (suit[i+1] == 2)
suit[i+1] = suit[i] = 1;
int answer = 0;
for (int v : suit)
if(v) ++answer;
return answer;
}
이 코드에서는 작은 순번부터 큰 순번대로 훑었지만, 반대로 훑어도 통과가 가능하다. 단, 이 때는 순서를 반대로 훑듯이 체육복도 반대로 큰 번호부터 확인해야 한다.
Comments