티스토리 뷰
칸 수가 한정되 있는 멀티탭에 여러 개의 전자기기 전원 콘센트를 꽂아야 할 때 콘센트를 가장 적게 뺄 수 있는 횟수를 구하는 문제
문제
기숙사에서 살고 있는 준규는 한 개의 멀티탭을 이용하고 있다. 준규는 키보드, 헤어드라이기, 핸드폰 충전기, 디지털 카메라 충전기 등 여러 개의 전기용품을 사용하면서 어쩔 수 없이 각종 전기용품의 플러그를 뺐다 꽂았다 하는 불편함을 겪고 있다. 그래서 준규는 자신의 생활 패턴을 분석하여, 자기가 사용하고 있는 전기용품의 사용순서를 알아내었고, 이를 기반으로 플러그를 빼는 횟수를 최소화하는 방법을 고안하여 보다 쾌적한 생활환경을 만들려고 한다.
예를 들어 3 구(구멍이 세 개 달린) 멀티탭을 쓸 때, 전기용품의 사용 순서가 아래와 같이 주어진다면,
- 키보드
- 헤어드라이기
- 핸드폰 충전기
- 디지털 카메라 충전기
- 키보드
- 헤어드라이기
키보드, 헤어드라이기, 핸드폰 충전기의 플러그를 순서대로 멀티탭에 꽂은 다음 디지털 카메라 충전기 플러그를 꽂기 전에 핸드폰충전기 플러그를 빼는 것이 최적일 것이므로 플러그는 한 번만 빼면 된다.
입력
첫 줄에는 멀티탭 구멍의 개수 N (1 ≤ N ≤ 100)과 전기 용품의 총 사용횟수 K (1 ≤ K ≤ 100)가 정수로 주어진다. 두 번째 줄에는 전기용품의 이름이 K 이하의 자연수로 사용 순서대로 주어진다. 각 줄의 모든 정수 사이는 공백문자로 구분되어 있다.
출력
하나씩 플러그를 빼는 최소의 횟수를 출력하시오.
풀이
처음에(1차원적으로) 접근했던 방법
같은 숫자는 어차피 계속 연결해 놓고 쓸 거니까 같은 숫자들을 중복제거 해 주고 남은 원소들의 개수에서 멀티탭 구멍의 개수를 뺀 나머지를 출력하면 될 것이라 생각했습니다. 하지만 예제만 맞고 다른 테스트 케이스에서는 틀림
두 번째로 접근한 방법
질문게시판 반례들을 참고하고 난 뒤, 첫 번째 방법으로 접근하는 것이 아니었다는 것을 깨달았으며...ㅎ 현재 꽂혀있는 콘센트 중 나중에 또 쓸 것이 있으면 가장 나중에 다시 쓰게 될 것을 먼저 빼는 방법으로 접근하기로 했습니다. 하지만 또다른 반례에 걸려서 26%에서 통과 못 함
마지막으로 접근한 방법
두 번째로 짰던 로직 자체가 틀린 것은 아니었으나 맨 처음 N개의 콘센트를 멀티탭에 꽂을 때 중복제거를 해 주지 않아서 틀렸습니다. 저는 입력으로 들어오는 숫자들 중 맨 처음 N개는 바로 멀티탭 배열에 삽입했는데, 이 때 중복 제거를 해 주지 않아서 같은 번호들이 줄줄이 꽂혀서 26%에서 틀렸던 것입니다... 저 부분 예외처리를 해 주니까 바로 통과되었습니다.
코드
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//남은 전자기기 중 멀티탭에 꽂혀 있는 전자기기와 같은 번호가 언제 처음으로 나오는 지 확인할 함수
int findFirstIndex(vector<int>& orders, int start, int num)
{
int index = 0;
for (int i = start; orders.size() > i; i++)
{
if (num == orders[i])
{
index = i;
break;
}
}
return index;
}
int main()
{
int N, K;
cin >> N >> K;
vector<int> orders; //전자기기를 꽂을 순서
vector<int> counts(K + 1, 0); //각 전자기기들의 남은 등장 횟수
vector<int> plug; //멀티탭 배열
for (int i = 0; K > i; i++)
{
int input;
cin >> input;
//멀티탭에 꽂힌 콘센트의 개수가 N개보다 적으면 바로 멀티탭에 꽂는다.
if (plug.size() < N)
{
//만약 이미 꽂혀있는 콘센트라면 넘어가고 꽂혀있지 않은 콘센트 일 때에만 꽂는다.
if (plug.end() == find(plug.begin(), plug.end(), input))
plug.emplace_back(input);
}
else
{
//멀티탭이 꽉찼으면 순서 리스트에 대기시킨다.
orders.emplace_back(input);
//남은 등장 횟수 증가
counts[input]++;
}
}
int answer = 0;
for (int i = 0; orders.size() > i; i++)
{
//꽂혀있으면 넘어감
if (plug.end() != find(plug.begin(), plug.end(), orders[i]))
{
//남은 등장 횟수 감소
counts[orders[i]]--;
continue;
}
bool change = true; //반복문 종료 후 교체해야 할 지 확인할 변수
int orderIndex = 0; //가장 나중에 등장하는 번호의 인덱스 저장용 변수
int indexToChange = 0; //교체할 멀티탭 인덱스
for (int j = 0; plug.size() > j; j++)
{
//꽂혀있는 번호가 나중에 등장할 예정이 없으면 이걸로 바꾼다.
if (0 == counts[plug[j]])
{
plug[j] = orders[i];
answer++;
counts[orders[i]]--;
change = false;
break;
}
//꽂혀있는 번호가 나중에 등장할 예정이면 몇 번째 뒤에 처음으로 나오는 지 찾는다.
int foundIndex = findFirstIndex(orders, i + 1, plug[j]);
//멀티탭에 꽂힌 번호들 중 가장 나중에 등장하는 번호를 찾는다.
if (orderIndex < foundIndex)
{
orderIndex = foundIndex;
indexToChange = j;
}
}
//위에서 찾은 번호를 바탕으로 바꿔준다.
if (change)
{
plug[indexToChange] = orders[i];
counts[orders[i]]--;
answer++;
}
}
cout << answer;
return 0;
}
'알고리즘 문제 풀이 > Greedy' 카테고리의 다른 글
[C++] 백준 온라인 저지 3109번 빵집 풀이 (0) | 2021.11.20 |
---|---|
[C++] 백준 온라인 저지 2720번 세탁소 사장 동혁 풀이 (0) | 2021.11.19 |
[C++] 백준 온라인 저지 11000번 강의실 배정 풀이 (0) | 2021.11.17 |
[C++] 백준 온라인 저지 2847번 게임을 만든 동준이 풀이 (0) | 2021.11.16 |
[C++] 백준 온라인 저지 1783번 병든 나이트 풀이 (0) | 2021.11.16 |
- Total
- Today
- Yesterday
- 영어공부
- 알고리즘
- 기초
- 프로그래머스
- 캐나다생활
- 너비우선탐색
- DFS
- c++
- 컴퓨터공부
- 코딩공부
- 스위프트플레이그라운드
- greedy
- 깊이우선탐색
- 컴퓨터
- 해커랭크
- 컴퓨터사이언스
- 프로그래밍
- 하드웨어
- C언어기초
- hackerrank
- 캐나다
- BFS
- c언어
- 다이나믹프로그래밍
- 아이패드
- 백준
- 그리디
- 애플
- 문제풀이
- dp
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |