티스토리 뷰
https://www.acmicpc.net/problem/1781
🐢 설명
제한기간이 있는 문제를 풀어서 최대한 많은 컵라면을 얻어야 되는 문제이다.
먼저 우선순위큐를 사용해서 컵라면을 가장 많이 주는 순서로 정렬하고 해당 문제의 마감날을 통해 해당날에 이미 문제를 풀었으면 그 전날 ~ 1일 까지 확인하면서 문제를 풀수 있는날을 찾아주면된다.
하지만 단순하게 우선순위큐에서 하나씩 뽑고 날짜 확인하고 이미 차있으면 배열을 돌면서 빈날을 확인하게 하면 시간초과가 나게 된다.
- 총 과제의 개수가 20만개인데 최악의 경우 20만개 * d-day 20만으로 O(N^2)이 된다.
Union-Find를 사용하면 이를 O(N)으로 풀수 있다. 어떻게 접근하면 되는지 정리해보자.
- d-day날이 총 과제의 개수 보다 크거나 같다면 전부 풀 수 있으므로 바로 컵라면 개수를 더한다.
- 아닐 경우
- day날 풀수 있다면 바로 그날 푼다. find(day)가 자신의 day와 같다면 풀 수 있음
- 풀고 나서 하루 전날과 합친다. union(find(전날), 당일)
- d-day날 풀수 없다면 ( find(day)가 자신의 day와 다름 ), 하루 전날의 parents인 find(day - 1)과 자신의 day의 parents인 find(day)와 비교한다.
- 전날의 parent가 자신의 day와 같다면 풀고나서 하루 전날의 parent에 합친다.
- 만약 그날이 0일 이라면 풀 수 없으므로 넘어간다. (해당 문제는 못품)
- day날 풀수 있다면 바로 그날 푼다. find(day)가 자신의 day와 같다면 풀 수 있음
자신의 기존 날에 풀수 있으면 풀고, 아니면 parents를 비교해서 O(1)로 이동해서 풀거나 / 0일 이라 못풀거나가 되므로 O(N)으로 풀이가 가능하다.
🐢코드
//package Gold이상문제_정리;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
public class Main {
static int N, LastDay;
static PriorityQueue<Cup> pq = new PriorityQueue<>(Comparator.comparingInt(c -> -c.count));
static int[] days;
private static class Cup {
int deadLine;
int count;
public Cup(int deadLine, int count) {
this.deadLine = deadLine;
this.count = count;
}
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
int dead = Integer.parseInt(st.nextToken());
int count = Integer.parseInt(st.nextToken());
pq.add(new Cup(dead, count));
LastDay = Math.max(LastDay, dead);
}
days = new int[LastDay + 1];
for (int i = 0; i < LastDay + 1; i++) days[i] = i;
int answer = solve();
System.out.println(answer);
}
private static int solve() {
int totalCup = 0;
int today;
while (!pq.isEmpty()) {
Cup now = pq.poll();
today = now.deadLine;
if (today >= N) {
totalCup += now.count;
continue;
}
if (find(today) == today) { // 당일 풀기 가능
union(find(today - 1), today);
totalCup += now.count;
} else if (find(today) != 0) { // 가능한 전일에 미리 풀기 (단, 0일이 아니라면)
today = find(today);
union(find(today - 1), today);
totalCup += now.count;
}
}
return totalCup;
}
private static int find(int now) {
if (days[now] == now) return now;
return days[now] = find(days[now]);
}
private static void union(int left, int right) {
left = find(left);
right = find(right);
days[days[right]] = days[left];
}
}
🐢 마무리
유니온 파인드를 이용해서 풀수 있는 재밌는 문제였다.
반응형
'알고리즘 > 백준' 카테고리의 다른 글
(백준) 맥주마시면서걸어가기_9205 [java] (0) | 2021.08.24 |
---|---|
(백준) 연료채우기_1826 [java] (0) | 2021.08.23 |
(백준) 최소 환승 경로 - 2021 [java] (0) | 2021.08.18 |
(백준) 달이 차오른다, 가자 - 1194 [java] (0) | 2021.08.17 |
(백준) 미친 아두이노 - 8972 [java] (0) | 2021.08.10 |
Comments
반응형
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday