블로그 옮겼습니다

BOJ 9007번 카누 선수 본문

Algorithm/Problem Solving

BOJ 9007번 카누 선수

sgc109 2017. 5. 2. 20:05

\(1\leq{N}\leq{1000}\)

\(1\leq{K}\leq{4\cdot{10^{7}}}\)

\(1\leq{w}\leq{10^7}\)


4개의 반이 있고 각 반의 학생수는 N명이며, 각 학생들의 몸무게 w 가 주어진다.

반 마다 한명 씩을 골라서 총 네명의 선수를 선발하는데 네 선수의 몸무게의 합이 K와 가장 근접하도록

선수들을 선발하여 그 몸무게의 합을 구하는 문제이다. 만약 답이 여러개라면 그중 작은 값을 답으로한다.


우선 가장 무식하게 푸는 방법이 완전탐색을 하는 방법이며 O(N^4) 이 걸린다. N이 최대 1000이기 때문에

무리가 있다. 그런데 조금만 생각을 해보면 세 개의 반에서 선수가 골라졌다면 남은 한 반에서는

K와 가장 가까운 학생을 고르면 되기 때문에 K-(세 반에서 뽑은 학생들의 몸무게의 합) 으로 이분탐색을 하면 된다.

하지만 그래도 O(N^3lgN) 의 시간 복잡도이기 때문에 너무 느리다.


일단 N이 세제곱되면 안된다.. 제곱은 되는데, 이럴 때 쓸 수 있는 아주 좋은 방법이 둘로 나누어 모든경우를 고려한뒤

둘을 합치는 방법이다. 두반만 고려하는 데에는 N^2 의 시간이 걸리기 때문에 두반씩 쪼개서 각각에 대해

모든 경우를 고려하는건 O(N^2)의 시간에 가능하다.

그렇다면 각각 둘을 어떻게 합칠까?


1반과 2반의 모든 학생들에 대해 각 반별로 한명씩 골라서 몸무게의 합을 구하는 방법은 ON^2) 가지 이다.

그러면 1반과 2반에서 한명씩 골라 몸무게를 합한것이 A라고 할 때,

나머지 두 반에서 고른 학생들의 몸무게의 합이 K-A 에 가장 가깝게 하면되는 것이다.

그럼 또 3반과 4반에서 각각 한명씩 골라 몸무게를 합친 값은 O(N^2)개가 있다.

그럼 이걸 정렬하고 K-A로 lower_bound 하면 O(N^2lgN) 에 답을 구할 수가 있다.

lower_bound 를 했을 때 나오는 위치를 pos 라고 했을 때

pos의 원소와 pos-1 번째 원소만 봐주면 되는데, 그 이유는 pos 번째놈은 K-A 이상의 수 일 것이다.

그러면 A와의 합은 K이상일텐데 그럼 pos+1 부터는 A와의 합이 K이상이기 때문에

A와 pos번째 놈의 합 이상이면서 K이상이기 때문에 pos보다 최적의 답이 될 수 없다.

그렇지만 pos-1은 더 최적의 답이 될 수 있기 때문에 봐줘야한다.

lower_bound 는 같은 원소가 여러개 있을 때 그 중 가장 첫번째 원소의 위치를 찾기 때문에

중복제거를 할 필요가 없이 pos와 pos-1만 보면 되는 것이다. 다른 문제였다면 중복제거를 해줄 필요가 있을 가능성이 있다.

그리고 사실 처음에는 중복제거를 꼭 해야하는줄 알고, 그리고 구현을 간단하게 하기 위해 set을 썼는데

시간복잡도가 같은데도 불구하고 3초안에 안돌아서 TLE가 났다.. 원인은 잘 모르겠다. 테케가 여러개라서 그렇다고하는데

아무리 그래도그렇지.. 원래 set이 좀 많이 느린가보다.


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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9+7;
const int INF = 0x3c3c3c3c;
const long long INFL = 0x3c3c3c3c3c3c3c3c;
 
int N, T;
vector<long long> g[2];
long long K;
long long A[4][1003];
int main() {
    scanf("%d",&T);
    while(T--){
        g[0].clear();
        g[1].clear();
 
        scanf("%lld%d"&K, &N);
        for(int i = 0 ; i < 4; i++){
            for(int j = 0 ; j < N; j++){
                scanf("%lld",&A[i][j]);
            }
        }
        for(int k = 0 ; k < 2; k++){
            for(int i = 0 ; i < N; i++){
                for(int j = 0; j < N; j++){
                    long long sum = A[2*k][i] + A[2*+ 1][j];
                    g[k].push_back(sum);
                }
            }
        }
 
        for(int i = 0 ; i < 2; i++) sort(g[i].begin(), g[i].end());
 
        long long ans = INFL;
        for(long long u1 : g[0]){
            auto it2 = lower_bound(g[1].begin(), g[1].end(), K - u1);
            if(it2 != g[1].end()) {
                long long sum = u1 + (*it2);
                if(abs(ans - K) > abs(sum - K)) ans = sum;
                if(abs(ans - K) == abs(sum - K) && ans > sum) ans = sum;
            }
            if(it2 != g[1].begin()) {
                it2--;
                long long sum = u1 + (*it2);
                if(abs(ans - K) > abs(sum - K)) ans = sum;
                if(abs(ans - K) == abs(sum - K) && ans > sum) ans = sum;
            }
        }
        printf("%lld\n", ans);
    }
 
    return 0;
}
 
cs


'Algorithm > Problem Solving' 카테고리의 다른 글

BOJ 10166번 관중석  (0) 2017.05.03
SCPC 2016 본선 A. 재활용  (0) 2017.05.02
BOJ 1044번 팀 선발  (0) 2017.05.02
Topcoder SRM 517 Div1(Medium) AdjacentSwaps  (0) 2017.05.01
제 5회 kriiicon UV(Unifying Values)  (0) 2017.05.01
Comments