Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
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
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

[백준] Greedy: 수 묶기 본문

코테준비

[백준] Greedy: 수 묶기

Seung__ 2022. 6. 1. 11:51

문제

길이가 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은 50보다 작은 자연수이다. 둘째 줄부터 N개의 줄에 수열의 각 수가 주어진다. 수열의 수는 -1,000보다 크거나 같고, 1,000보다 작거나 같은 정수이다.

출력

수를 합이 최대가 나오게 묶었을 때 합을 출력한다. 정답은 항상 231보다 작다.

 

문제 접근

주어진 수열에서 중복되지 않게 임의의 두 수를 묶어서 곱할 수 있을 때, 가장 큰 합을 반환해야 한다.\

합이 가장 크도록 만들기 위해서는 양수인 경우 가장 큰 수들 끼리 곱해야 하고, 음수인 경우 가장 작은 수 끼리 곱해야 한다.

 

그럼 여기서 두 가지 방법이 있을 수 있다.

 

1. 한 배열에 모든 수를 넣고 정렬한 뒤 두 개의 인덱스를 활용해서 탐색하며 합을 구하는 방법

2. 양수, 음수, 0 모두 다른 배열에 넣어놓고 정렬한  후 각 배열마다 탐색 및 합 구하기.

 

1번의 경우 반복문 한 번으로 끝낼 수 있다. 하지만 0이 있는지 없는지 있으면 0의 위치를 알아야 하고, 양수 or 음수의 개수가 짝수일 때 홀수일 때 경우를 나누기 불편하다. 1번으로 작성하다가 if문만 계속 늘어나서 중단했다.

 

2번의 경우 상대적으로 간단하다. 양수, 음수, 0으로 나누고 양수, 음수에서 최대값을 단순히 뽑아내면 된다.

 

 양수인 경우 내림차순으로 정렬한 뒤, 반복문을 돌면서 두 개씩 곱해주면 된다. 이 때 개수가 짝수&홀수인 경우를 주의.

또한 1을 만나는 경우 곱하는 것 보다 더하는게 이득이다. Math.max(곱, 합)으로 처리해 줬다. 또는 직접 1인지 판단해도 가능.

 

음수인 경우 오름차순으로 정렬한 뒤, 두 개씩 곱해주면 된다.

이때 마지막 음수를 따로 보관하자. 왜냐하면 0이 있을 경우 마지막 음수는 0으로 처리가 가능하다.

 

이 때 양수, 음수 모두 개수가 1개일 경우 처리할 수 있도록 로직이 필요하다.

 

풀이 코드

package baekjun;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;

public class _1744 {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int N = Integer.parseInt(br.readLine());
        ArrayList<Long> overZero = new ArrayList<>();
        ArrayList<Long> underZero = new ArrayList<>();
        int zero = 0;

        long answer = 0;

        for (int i=0; i<N; i++){
            long n = Long.parseLong(br.readLine());
           if (n > 0){
               overZero.add(n);
           }else if (n < 0){
               underZero.add(n);
           }else {
               zero++;
           }
        }

        long sumPlus = 0;
        overZero.sort(Collections.reverseOrder());
        for (int i=0; i< overZero.size() ; i+=2){

            if (overZero.size() == 1 && i == 0){
                sumPlus += overZero.get(i);
            }else if (i == overZero.size()-1){
                sumPlus+= overZero.get(i);
            }else {
                sumPlus += Math.max(overZero.get(i)*overZero.get(i+1), overZero.get(i)+overZero.get(i+1));
            }

        }

        long sumMinus = 0;
        underZero.sort((n1, n2)-> (int) (n1-n2)); //오름차순
        long tmp = 0; // 음수가 한개 남으면 잠시 보관, 0이랑 곱할 수 있음.
        for (int i=0; i< underZero.size() ; i+=2){

          if (i == underZero.size()-1){ //size가 1인 경우 바로 tmp에 저장하고 루프 나감.
                tmp = underZero.get(i);
            } else{
                sumMinus += underZero.get(i)*underZero.get(i+1);
            }
        }

        if (zero == 0){
            answer = sumPlus + sumMinus + tmp;
        }else{
            //0이 존재하는 경우
            //음수의 개수가 2개 이상인 경우 마지막 수(tmp)제외하고 sumMinus에 반영됨, 마지막수(tmp)는 0과 곱해져서 무의미
            //음수의 개수가 1개인 경우, tmp에 저장되고 0과 곱해짐.
            //음수의 개수가 0개이 경우, sumMinus = 0
            answer = sumPlus + (underZero.size() > 1 ? sumMinus : 0);
        }

        System.out.println(answer);
    }
}

 

'코테준비' 카테고리의 다른 글

[백준] Greedy: 행렬  (0) 2022.06.01
[백준] Greedy: 5와 6 의 차이  (0) 2022.06.01
[백준] Greedy: 기타줄  (0) 2022.05.30
[백준]Greedy: 보석도둑  (0) 2022.05.29
[백준] Greedy: 캠핑  (0) 2022.05.28
Comments