Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- SQLD / SQLP
- 단계별로 풀어보기
- pypy3
- GROUP BY 절
- java
- BOJ
- JAVA 11
- 파이썬
- 백준
- Python
- 자바
- 응용
- Java11
- 기초
- 이론
- baekjoon
- programmers
- level1
- 명품 자바 프로그래밍
- HAVING 절
- 기본
- 개념
- 코딩테스트
- 공공데이터
- 헤드퍼스트 디자인패턴
- 기초100제
- SELECT 절
- Codeforces Round #802 (Div. 2)
- Codeup
- Python 3
Archives
- Today
- Total
Development Project
[ Baekjoon - 11/08 ] - 1781번: 컵라면 본문
1781번: 컵라면
상욱 조교는 동호에게 N개의 문제를 주고서, 각각의 문제를 풀었을 때 컵라면을 몇 개 줄 것인지 제시 하였다. 하지만 동호의 찌를듯한 자신감에 소심한 상욱 조교는 각각의 문제에 대해 데드라
www.acmicpc.net
- 소요 시간 : 2시간 반
컵라면
시간 제한메모리 제한제출정답맞힌 사람정답 비율
2 초 | 256 MB | 9247 | 2741 | 2059 | 31.122% |
문제
상욱 조교는 동호에게 N개의 문제를 주고서, 각각의 문제를 풀었을 때 컵라면을 몇 개 줄 것인지 제시 하였다. 하지만 동호의 찌를듯한 자신감에 소심한 상욱 조교는 각각의 문제에 대해 데드라인을 정하였다.
문제 번호데드라인컵라면 수1 | 2 | 3 | 4 | 5 | 6 | 7 |
1 | 1 | 3 | 3 | 2 | 2 | 6 |
6 | 7 | 2 | 1 | 4 | 5 | 1 |
위와 같은 상황에서 동호가 2, 6, 3, 1, 7, 5, 4 순으로 숙제를 한다면 2, 6, 3, 7번 문제를 시간 내에 풀어 총 15개의 컵라면을 받을 수 있다.
문제는 동호가 받을 수 있는 최대 컵라면 수를 구하는 것이다. 위의 예에서는 15가 최대이다.
문제를 푸는데는 단위 시간 1이 걸리며, 각 문제의 데드라인은 N이하의 자연수이다. 또, 각 문제를 풀 때 받을 수 있는 컵라면 수와 최대로 받을 수 있는 컵라면 수는 모두 231보다 작거나 같은 자연수이다.
입력
첫 줄에 숙제의 개수 N (1 ≤ N ≤ 200,000)이 들어온다. 다음 줄부터 N+1번째 줄까지 i+1번째 줄에 i번째 문제에 대한 데드라인과 풀면 받을 수 있는 컵라면 수가 공백으로 구분되어 입력된다.
출력
첫 줄에 동호가 받을 수 있는 최대 컵라면 수를 출력한다.
예제 입력 1 복사
7
1 6
1 7
3 2
3 1
2 4
2 5
6 1
예제 출력 1 복사
15
- 문제를 읽고 이해하기
- 제한
- 시간 : 2초
- 메모리 : 256MB
- 문제
- N개의 숙제(1 ≤ N ≤ 200,000), 1 ≤ 각 문제를 풀때 받는 컵라면과 최대로 받을 수 있는 컵라면 수 ≤ 2^31
- 각 문제마다 문제번호, 마감시간, 컵라면의 수가 각각 주어진다.
- 문제를 풀면 컵라면을 얻을 수 있지만, 풀기위해서는 각 문제의 데드라인 내에 풀어야한다.
- 이때, 최대로 얻을 수 있는 컵라면의 개수를 출력하는 문제
- 이해
- 문제가 굉장히 단순해 보였고, 그리디를 써야할것같다고 생각했다.
- 제한
- 문제를 익숙한 용어로 재정의와 추상화
- 입출력 형식에 맞게, 각 변수의 조건과 시공간 조건을 적어보았고, 입출력 예제를 다시 그려보았다.
- 그리디를 써야할 것 같았는데, 생각보다 예제가 단순해보여서 잘만하면 정렬로 끝낼 수 있을거라 생각하고 첫번째 접근을 시도했다..
- 문제를 어떻게 해결할 것인가
- 1st 접근 - 가짜 그리디, 정렬만..)
- 가짜 그리디라 한 이유는 2번째 접근까지 하고나서야 이 그리디적 접근은 틀렸다고 생각했기 때문...
- 우선 모든 숙제를 푸는 시간은 전부 1의 시간(부럽..)이기때문에, 순서가 가장 핵심이라 생각했다.
- 우선 시간이 겹치는 경우는 데드라인이 같을때 뿐이므로, 데드라인으로 정렬해야했고
컵라면의 개수를 큰것을 선택해야하므로
마감시간은 오름차순, 컵라면의 수는 내림차순으로 정렬해야한다 생각했다. - 그리고 해당 입출력 예제의 경우, 정렬만해도 딱 떨어지도록 답이 도출되어서... 정렬만으로 가능했다.
( 하지만 이는 반례를 보고 불가하다는것을 깨달았지만.. ) - 사실 구현방식을 해쉬맵(딕셔너리)로 할지 리스트를 행렬처럼 연결할지 좀 고민했는데 손이 좀 더 익숙한 리스트로 구현해보았다.
- 틀렸습니다를 얻었는데 너무 이해가 가질 않아서 질문하기에서 반례를 찾으러 들어갔다.
그런데 반례 두개가 확실하게 내 코드가 이상함을 증명하는 아이들이었다.. 다들 나같은 접근을 했구나..
( 각 반례는 사진의 왼쪽 하단의 두개이다. ) - 하나는 같은 마감기한이라면 꼭 하나만을 고르지 않아도 가능한 경우가 있음을 증명하는 케이스였고,
나머지 하나는 가능한 마감기한이 있어도, 긴 마감이지만 더 좋은 컵라면의 개수를 가진다면 해당 아이를 채택해야함을 보여주는 케이스였다. - 위 반례들을 보며 내 코드가 잘못된 이유를 알아내어 첫번째 케이스는 보완이 간단했지만, 두번째 케이스는 도저히 현 코드로서는 수정이 불가해보였다..ㅠ
- 그렇게 두번째, 진짜 그리디를 쓰게 되었다.
- 2nd 접근 - 진짜 그리디, 정렬과 큐를 사용)
- 그리디스러운 그리디접근법을 하기위해, 우선 정렬을 해야했는데 정렬은 그대로 가는게 맞아보였고 이제 그 뒤 처리를 어떤식으로 각색하느냐가 문제였다.
- 시나리오를 생각해보았는데,
위 2번째 반례의 경우, 긴 마감이어도 더 좋은 컵라면의 수를 가진다면 채택해야함 이였기 때문에
그러면 어떻게 현재 숙제들 중 최적의 숙제들만 보관할 수 있을지 고민해보았다.. - 아무리 생각해도... 한번에 최적의 숙제들을 정렬만을 통해서는 구하기가 어려워보여서, 가장 Worst, 즉 가장 안타까운 개수의 컵라면을 받는 숙제를 먼저 꺼내서 버릴수만 있으면 될 것이라 생각했다.
결국 가장 안타까운 개수도 정렬을 통해 얻을 수 있지만, 이미 리스트는 정렬을 했기 때문에 또 정렬은 무의미하므로 현재까지의 결과값만 따로 저장할 공간이 필요하다 생각했고 이를 위해 우선순위 큐를 채택했다! - 현재까지 실행된 묶음(pq가 보관)에서 현재 실행시간 이내에 가능한 아이가 나타나면, pq에서는 가장 컵라면의 개수가 안타까운 아이와 비교해서 pq아이가 더 작다면 얘를 버리고 가능한 아이를 넣는 식으로 짜면 될것이라 생각했다!
- 1st 접근 - 가짜 그리디, 정렬만..)
- 위 계획을 검증
- 컵라면의 최대개수를 출력하는 문제이므로, 이때까지 보관한 컵라면의 개수들을 비교하며 순간순간의 최대값들만 취하는 형태이므로 가능하다.
- 게다가 이때까지 보관한 모든 숙제들과 새롭게 등장한 숙제를 비교하는 것이 아닌, 가장 안타까운 컵라면의 개수를 가진 숙제와 비교하는 것이기 때문에 좀더 안정적으로 수행될 수 있을거라 생각한다.
- 계획 수행 (실제코드 작성)
- Java 11
-
import java.io.*; import java.util.*; public class Main { static int N; static long nowCupNoodleCount = 0; static Homework[] hArr; static PriorityQueue<Integer> pq = new PriorityQueue<Integer>(); public static void main(String args[]) throws NumberFormatException, IOException { //System.setIn(new FileInputStream("src/input.txt")); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); N = Integer.parseInt(br.readLine()); hArr = new Homework[N]; for(int n = 0; n < N; n++) { StringTokenizer st = new StringTokenizer(br.readLine()); hArr[n] = new Homework(Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken())); } Arrays.sort(hArr); for(Homework Homework : hArr) { // 모든실행은 1단위이므로, pq사이즈는 현재 실행 시간임 if(pq.size() < Homework.deadLine) { pq.offer(Homework.noodlesNum); } // 해당 마감기한이 현재 실행시간과 같다면 else if(pq.size() == Homework.deadLine) { // 담아둔 숙제 중 작은 컵라면의 수를 현재 컵라면개수와 비교했을때, 현재 들어온 컵라면이 더 많다면 if(pq.peek() < Homework.noodlesNum) { // 삭제하고 pq.poll(); // 넣기 pq.offer(Homework.noodlesNum); } } } // 실행한 숙제의 컵라면 수 합산 while(!pq.isEmpty()) { nowCupNoodleCount += pq.poll(); } bw.write(nowCupNoodleCount+"\n"); bw.flush(); } } class Homework implements Comparable<Homework> { int deadLine; int noodlesNum; Homework(int deadLine, int noodlesNum) { this.deadLine = deadLine; this.noodlesNum = noodlesNum; } @Override public int compareTo(Homework o) { // 마감기한 오름차순, 컵라면 내림차순 if(deadLine == o.deadLine) { return o.noodlesNum - noodlesNum; } return deadLine - o.deadLine; } @Override public String toString() { return "Homework{" + "deadLine=" + deadLine + ", noodlesNum=" + noodlesNum + '}'; } }
-
- Python 3
-
import sys import heapq input = sys.stdin.readline def solution(): N = int(input()) hArr = [list(map(int, input().split())) for _ in range(N)] # 마감기한 오름차순, 컵라면 수 내림차순 hArr = sorted(hArr, key=lambda x: (x[0], -x[1])) pq = [] for h in range(len(hArr)): # 모든실행은 1단위이므로, pq사이즈는 현재 실행 시간임 if len(pq) < hArr[h][0]: heapq.heappush(pq, hArr[h][1]) # 해당 마감기한이 현재 실행시간과 같다면 elif len(pq) == hArr[h][0]: # 담아둔 숙제 중 작은 컵라면의 수를 현재 컵라면개수와 비교했을때, 현재 들어온 컵라면이 더 많다면 if pq[0] < hArr[h][1]: # 삭제하고 넣기 heapq.heappop(pq) heapq.heappush(pq, hArr[h][1]) # 실행한 숙제의 컵라면 수 합산 ans = 0 while pq: ans += pq.pop() print(ans) if __name__ == '__main__': solution()
-
- Java 11
- 회고하기
- "그리디를 풀기 위해선 정렬을 하고 들어가야한다" 정도만 터득하고있어서 그리디임을 알아도 바로 그리디를 구현해내지는 못하는 것 같다ㅜ 해당 문제의 특징을 파악하여 적절한 알고리즘을 채택하는 연습을 좀 더 해야할듯 싶다. 이참에 다시 이론책을 펴봐야할것같기도하고.
- 결과

'CodingTest > Baekjoon' 카테고리의 다른 글
[ Baekjoon - 11/10 ] - 1915번: 가장 큰 정사각형 (0) | 2022.11.10 |
---|---|
[ Baekjoon - 11/10 ] - 1987번: 알파벳 (0) | 2022.11.10 |
[ Baekjoon - 11/07 ] - 2169번: 로봇 조종하기 (2) | 2022.11.07 |
[ Baekjoon - 11/02 ] - 1334번: 다음 팰린드롬 수 (0) | 2022.11.02 |
[ Baekjoon - 11/01 ] - 10942번: 팰린드롬? (0) | 2022.11.01 |
Comments