[결정 문제] [시간 초과] WITHDRAWAL 수강 철회
2021. 1. 22. 00:17ㆍ알고리즘/알고리즘 문제 [hard]
1. 최적 문제
n개의 강의 중에서 k개이상의 강의만 남겨놓고 철회할 때
최소 누적 등수를 구하는 문제이다.
이것은 k개 까지 하나 하나 철회해보면서 누적등수를 계산하고 그 중에서
최소값을 구하는 최적화 문제이다.
이 문제를 결정문제로 변형해보자
2. 결정 문제
isOkay(r) = r의 누적등수가 주어졌을 때 이것보다 작은값이 있는가?
위와 같은 식을 토대로 함수를 작성해 보았다.
isOkay는 k개 까지 모든 경우를 세서 주어진 누적등수보다 작은 값이 나오면 true
작은 값이 나오지 않을 경우 false를 반환한다.
하지만 이 함수는 최악의 경우 2^1000 개를 카운트하므로 당연히 제출한 코드는 시간 초과를 내뿜었다.
이전 꺼랑 비교해서 더 큰값이 나올 경우 가지치기하려는 생각도 해봤지만 답을 구하는것과 상관이 없었다.
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
29
30
31
32
33
34
35
|
int T, N, K;
std::vector<std::pair<int, int>> lec;
//r c
bool isOkay(double R, int current, int select, int rsum, int csum)
{
if(select < K) return false;
if(R >= (double)rsum/csum) return true;
if(current < 0) return false;
if(isOkay(R, current-1, select-1, rsum - lec[current].first, csum - lec[current].second)) return true;
if(isOkay(R, current-1, select, rsum, csum)) return true;
return false;
}
double optimize()
{
double lo = 0, hi = 1.0;
int rsum = 0, csum = 0;
std::sort(lec.begin(), lec.end());
for(int i = 0; i < N; i++)
{
rsum += lec[i].first;
csum += lec[i].second;
}
for(int i = 0; i < 100; i++)
{
double mid = (hi + lo)/2.0;
if(isOkay(mid, N-1, N, rsum, csum)) hi = mid;
else lo = mid;
}
return hi;
}
|
cs |
3. 수학식을 바꿔보자
책에서는 비율을 구하는 대신에 분모를 없애 문제를 간단하게 만들었다.
sum(r)/sum(c) <= x
sum(r) <= x * sum(c)
0 <= sum(x * ci) - sum(ri)
0 <= sum(x * ci - ri)
즉 x*ci - ri 를 모두 구한다음
가장 큰것들 중 k~n개를 뽑아 더하면 0이상이 나오는가? 를 판단하면된다.
이것은 O(nlgn)만에 수행되어 이전 보다 엄청 빠르게 답을 내놓는다.
'알고리즘 > 알고리즘 문제 [hard]' 카테고리의 다른 글
[트리] [시도 x]FORTRESS 요새 (0) | 2021.02.04 |
---|---|
[수치 해석][삼분법][시도X] FOSSIL 꽃가루 화석 (0) | 2021.01.23 |
[조합 탐색][시도x]BOARDCOVER2 게임판 덮기2 (0) | 2021.01.20 |
[탐욕법] [시도x] MINASTIRITH 미나스 아노르 (0) | 2021.01.19 |
[동적 계획법][시도X] GENIUS 지니어스 (0) | 2021.01.18 |