1744. 수 묶기


문제

길이가 N인 수열이 주어졋을 때, 그 수열의 합을 구하려고 한다. 하지만, 그냥 그 수열의 합을 모두 더해서 구하는 것이 아니라, 수열의 어떤 인접한 원소끼리를 묶으려고 한다. 어떤 수를 묶으려고 할 때, 위치에 상관없이 묶을 수 있다. 하지만, 같은 위치에 있는 수(자기 자신)를 묶는 것은 불가능하다. 그리고 어떤 수를 묶게 되면, 수열의 합을 구할 때, 묶은 수는 서로 곱한 후에 더한다.

예를 들면, 어떤 수열이 {0, 1, 2, 4, 3, 5}일 때, 그냥 이 수열의 합을 구하면 0+1+2+4+3+5 = 15이다. 하지만, 2와 3을 묶고, 4와 5를 묶게 되면, 0+1+(2*3)+(4*5) = 27이 되어 최대가 된다.

수열의 모든 수는 단 한번만 묶거나, 아니면 묶지 않아야한다.

수열이 주어졌을 때, 수열의 각 수를 적절히 묶었을 때, 그 합이 최대가 되게 하는 프로그램을 작성하시오.

입력

첫째 줄에 수열의 크기 N이 주어진다. N은 10,000보다 작다. 둘째 줄부터 N개의 줄에, 수열의 각 수가 주어진다. 수열의 수는 -10,000보다 크거나 같고, 10,000보다 작거나 같은 정수이다.

출력

수를 적절히 묶어 그 합이 최대값을 출력한다. 정답은 항상 231보다 작다.

예제 입력 

4
-1
2
1
3

예제 출력 

6



1. 접근


그리디 알고리즘. 여러 수들을 묶거나 더해서 가장 큰 수를 만들려면 가장 큰 두 수(양수) 나 가장 작은 수 (음수)들을 묶어야 한다.



2. 풀이


0 < a < b < c < d 인 수 네 개를 규칙에 따라 가장 큰 합을 만들려면 어떻게 해야 할까?

1) a * b + c * d

2) a * c + b * d

3) a * d + b * c


직관적으로 1번이 가장 큰걸 알고 있다.

1번과 2번을 빼면 a * (b - c) > d * (b - c) 이므로 1번이 더 크다.

1번과 3번을 빼면 a * (b - d) > c * (b - d) 이므로 1번이 더 크다. 


음수에 양수를 곱해봤자 전체 합은 작아만지므로 음수는 가능한 음수와만 곱해야함이 자명하다.


따라서 음수와 양수를 따로 생각해서 가장 큰 수 두 개씩 묶어나가면 되겠다.


다만 1과 0은 예외적인 수인데, 0은 음수를 묶어나가고 남은 짜투리에 곱해서 0으로 만드는데 사용하고,

1은 곱해봤자 총 합을 늘리는데 도움이 되지 않으므로 1은 더하는데만 사용하면 되겠다.



3. 코드


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 <stdio.h>
#include <queue>
#include <vector>
#include <functional>
using namespace std;
 
int n, i, tmp, z, one, as, bs, sum;
priority_queue<intvector<int>, greater<int>> a;
priority_queue<intvector<int>, less<int>> b;
 
int main() {
    scanf("%d"&n);
    for (i = 0; i < n; ++i) {
        scanf("%d"&tmp);
        if (tmp == 1)
            one++;
        else if(tmp > 0)
            a.push(tmp);
        else if (tmp < 0)
            b.push(tmp);
        else
            z++;
    }
    if (a.size() % 2 == 1)
        a.push(1);
    if (b.size() % 2 == 1)
        z == 0 ? b.push(1) : b.push(0);
 
    as = a.size() / 2;
    bs = b.size() / 2;
    for (i = 0; i < as; ++i) {
        tmp = a.top();
        a.pop();
        sum += a.top()*tmp;
        a.pop();
    }
    for (i = 0; i < bs; ++i) {
        tmp = b.top();
        b.pop();
        sum += b.top()*tmp;
        b.pop();
    }
    printf("%d", sum + one);
    return 0;
}
cs


양수들의 큐와 음수들의 큐를 처리한 나머지 짜투리를 예외처리하기 귀찮으므로 추가로 1이나 0을 넣어주는 센스

'알고리즘 > Greedy 그리디' 카테고리의 다른 글

백준) 1931 회의실배정  (0) 2017.07.25
백준) 1946 신입 사원  (0) 2017.07.25
백준) 4796 캠핑  (0) 2017.07.23
백준) 11047 동전 0  (0) 2017.07.23
그리디 알고리즘 도입  (0) 2017.07.23

+ Recent posts