티스토리 뷰

IOI/IOI2020

IOI2020 Day1 tickets 풀이

전명우 2020. 9. 22. 21:11

문제 및 채점: oj.uz

 

티켓은 N 개의 색 중 한 가지 색을 띄며, 각 색마다 M 개의 티켓이 있다. 티켓에는 수가 적혀있는데, 티켓에 적힌 수는 서로 다를 수 있다. K 번의 라운드를 진행할거고 각 라운드마다 N 개의 서로 다른 색의 티켓을 뽑고, 어떤 수 b를 임의로 정해 bN 개의 티켓에 적힌 수와 차이값을 구해 모두 더한 값을 S라 하자. 이때, S의 값이 최소가 되도록 수 b를 정한다. 한 라운드에 쓰인 티켓은 소멸되기 때문에 한 티켓은 최대 한 라운드에서만 사용 가능하다. 각 라운드마다 티켓을 적절히 골라 모든 라운드의 S 값들을 다 더한 값을 최대화하는 문제다. 편의상 N은 짝수다.

 

우선, 각 라운드에서 S 값을 계산할 때 문제에서 정의한 방법 말고 쉬운 계산 방법을 찾아야 한다. 한 라운드에서 뽑힌 티켓들에 적힌 수를 a1,a2,,aN (a1a2aN)이라고 하자. S 값은 다음과 같이 간단하게 구할 수 있다.

S=aN+aN1++aN2+1aN2a1

즉, 라운드에서 뽑힌 티켓 중에 적힌 수가 큰 편에 속하는 수들은 S 값에 더해지고, 작은 편에 속하는 수들은 S 값에서 빼진다.

 

i 번 색을 띄는 티켓 M 개 중에 gi 개의 티켓이 라운드에서 큰 편에 속했다고 하면, Kgi 개의 티켓이 라운드에서 작은 편에 속했다. 또한 S의 합이 최대화 되기 위해서는 티켓 M 개 중에 적힌 수가 큰 상위 gi 개의 티켓이 뽑혀야 되고, 적힌 수가 작은 하위 Kgi 개의 티켓이 뽑혀야 된다. 그리고 giK 이며, gi의 합은 NK2가 되어야 한다.

 

1iN인 모든 i에 대해 S의 합이 최대가 되는 gi를 욕심쟁이 기법으로 구할 것이다. 우선 처음에 모든 gi0이라고 하고, i 번 색의 j 번째 티켓에 적힌 수를 A[i][j]라고 하자. (A[i][j]A[i][j+1]) 그러면 S의 합의 초기값은 A[i][1]A[i][2]A[i][K]의 합이 된다. 이제 gi를 적절히 증가시켜 gi의 합을 NK2로 만들 것이다. gi의 값을 1 증가하면, S의 합은 A[i][gi+1]+A[i][Kgi] 만큼 증가하게 된다. S의 합이 최대한 증가하도록 매 순간 증가량이 가장 크도록 gi 값을 1씩 증가시켜주면 S의 합의 최대값을 구할 수 있다.

 

이제 구체적으로 뽑힌 티켓이 어떤 라운드에 뽑혔는지 구하는 문제가 남았다. 라운드에서 작은 편에 속하는 수들은 라운드에서 큰 편에 속하는 수들보다 크지 않은 점을 이용하여 이 문제 또한 욕심쟁이 기법으로 해결할 수 있다.

 

이 문제를 해결하는 시간복잡도는 O(NKlgNK)이다.

 

코드 보기
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
#include <bits/stdc++.h>
#include "tickets.h"
using namespace std;
 
#define mt make_tuple
typedef pair<int, int> pii;
typedef tuple<int, int, int> tiii;
 
typedef long long lld;
 
lld find_maximum(int K, vector<vector<int>> A)
{
    int N = A.size();
    int M = A[0].size();
    lld ans = 0;
    vector<vector<int>> answer(N, vector<int>(M, -1));
    vector <tiii> order;
    for (int i=0;i<N;i++){
        for (int k=0;k<K;k++) ans -= A[i][k];
        for (int k=1;k<=K;k++) order.push_back(mt(A[i][M-k]+A[i][K-k], i, M-k));
    }
    sort(order.begin(), order.end());
    vector <int> cnt(N, 0), lpt(N, 0);
    for (int i=1;i<=K*N/2;i++){
        auto [v, p, q] = order[order.size()-i];
        ans += v; cnt[p]++;
    }
    for (int k=0;k<K;k++){
        vector <pii> arr;
        for (int i=0;i<N;i++) arr.push_back({-cnt[i], i});
        sort(arr.begin(), arr.end());
        for (int i=0;i<N/2;i++){
            int t = arr[i].second;
            assert(cnt[t] > 0);
            answer[t][M-cnt[t]] = k;
            cnt[t]--;
        }
        for (int i=N/2;i<N;i++){
            int t = arr[i].second;
            answer[t][lpt[t]++] = k;
        }
    }
    allocate_tickets(answer);
    return ans;
}

'IOI > IOI2020' 카테고리의 다른 글

IOI2020 Day2 mushrooms 풀이  (0) 2020.09.24
IOI2020 Day2 biscuits 풀이  (0) 2020.09.23
IOI2020 Day2 stations 풀이  (0) 2020.09.23
IOI2020 Day1 plants 풀이  (0) 2020.09.22
IOI2020 Day1 supertrees 풀이  (0) 2020.09.22
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
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
글 보관함