알고리즘/[ Baekjoon ]

[ BOJ ][JAVA][10427] 빚

kim.svadoz 2021. 5. 27. 17:33
반응형

https://www.acmicpc.net/problem/10427

시간 제한 메모리 제한 제출 정답 맞은 사람 정답 비율
1 초 256 MB 106 52 48 56.471%

문제

민균이에게는 ‘빚쟁이’ 라는 별명이 있다. 이 별명은 악덕 사채업을 하는 김우현연구소에서 민균이가 빌린돈을 잘 갚지 않는다고 하여 붙여준 이름이다.

민균이가 최근 N (1 ≤ N ≤ 4000) 번 돈을 빌렸고, 그때마다 빌린 돈이 각각 A(1), A(2), …, A(N) (1 ≤ A(i) ≤ 104) 라고 하자. 악덕 사채업소 김우현 연구소는 이름만큼이나 빌린 돈을 갚는 방식이 독특하다.

먼저, 김우현 연구소가 민균이에게 M번 (1 ≤ M ≤ N) 의 빚을 갚으라고 명령하면, 민균이는 N번중 아무렇게나 M 번을 고르고, 고른 것 중에서 가장 많은 돈을 빌렸을 때 빌린돈 x M 을 갚아야 한다. 이렇게 하면 민균이가 김우현 연구소에 갚아야 하는 돈은 (빌린돈 + 추가적으로 더 갚아야 할 돈) 이 된다. 예를 들어, 민균이가 3번 돈을 빌렸고, 각각 2, 5, 3 의 돈을 빌린 경우, 김우현 연구소가 2번의 빚을 갚으라고 명령하면, 민균이가 첫 번째, 두 번째를 선택했을 경우 갚아야 하는 돈은 5 x 2 = 10 이 되고, 추가적으로 더 갚아야 할 돈은 10 – (2 + 5) = 3 이 된다. 만약 민균이가 첫 번째, 세 번째를 선택하면, 갚아야 하는 돈은 3 x 2 = 6 이 되고, 추가적으로 더 갚아야 할 돈은 6 – (2 + 3) = 1 이 된다.

민균이는 이런 김우현 연구소가 괘씸하여 어떻게든 추가적으로 더 갚아야 할 돈을 최소한으로 하고 싶어 한다. S(M) 을 김우현 연구소가 민균이에게 M번의 빚을 갚으라고 명령했을 때 민균이가 M 번을 선택하여 추가적으로 갚아야 하는 돈의 최솟값이라고 하자.

예를 들어 N = 5 이고, 민균이가 N번동안 빌린 돈이 A = {1, 5, 4, 3, 8} 인 경우,

  • S(1) = 0 (어떻게 선택해도 추가적으로 갚을 돈은 없음)
  • S(2) = 1 (2, 3일 분을 갚거나 3, 4일분을 갚는 경우 추가적으로 1을 더 갚으면 됨)
  • S(3) = 3 (2, 3, 4 일분을 갚는 경우 추가적으로 3을 더 갚으면 됨)
  • S(4) = 7 (1, 2, 3, 4일분을 갚는 경우 추가적으로 7을 더 갚으면 됨)
  • S(5) = 19 (1, 2, 3, 4, 5일분을 갚는 경우 추가적으로 19를 더 갚아야 됨)

이제 여러분이 할 일은 민균이가 돈을 빌린 횟수 N 과 N번동안 각각 빌린 돈 A(1), A(2), …,A (N) 이 주어 졌을 때, S(i) 의 합 S(1),S(2),…,S(N) 을 구하는 것이다.

입력

먼저 테스트케이스의 수 T가 주어지고 이어져서 각각의 줄에 N 과 A(i) 의 정보가 차례대로 주어진다.

출력

각각의 줄에 대해 S(1) + … + S(N) 을 구한다. 중간 과정 및 답은 int 범위를 초과할 수 있으므로, 64bit 정수형(long long: C/C++, long: Java) 를 이용해야 한다. 입출력은

C/C++: printf("%lld\n",answer);
Java : System.out.println(answer);

로 할 수 있다.

예제 입력 1

3
5 1 5 4 3 8
3 1 2 3
6 3 4 1 6 8 1

예제 출력 1

30
4
51

접근

처음엔 조합을 사용해서 모든 경우의 수를 뽑아서 계산했는데, TLE가 났다.
시간을 줄이기 위해 고민한 결과
각 배열의 원소의 차이가 적은 구간이 가장 최소값을 뽑아 낼 수 있는 구간임을 찾아내어 쉽게 구현하였다.

코드

/*
    빚
    슬라이딩 윈도우, 규칙찾기, 구현
*/
import java.io.*;
import java.util.*;
public class p10427 {
    static int t, n;
    static int[] arr;
    static boolean[] visited;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;
        t = Integer.parseInt(br.readLine());
        StringBuilder sb = new StringBuilder();
        while (t-- > 0) {
            st = new StringTokenizer(br.readLine());
            n = Integer.parseInt(st.nextToken());
            arr = new int[n + 1];
            visited = new boolean[n + 1];
            for (int i = 1; i <= n; i++) {
                arr[i] = Integer.parseInt(st.nextToken());
            }
            sb.append(solve()).append("\n");
        }
        System.out.println(sb.toString());
    }
    static long solve() {
        long ret = 0;
        Arrays.sort(arr);
        for (int i = 2; i <= n; i++) {
            ret += func(i);
        }
        return ret;
    }

    // 슬라이딩 윈도우를 이용해 최소값 찾아내기 1 3 4 5 8 
    static long func(int dist) {
        long min = Integer.MAX_VALUE;
        for (int i = 1; i <= n - dist + 1; i++) {
            min = Math.min(min, calc(i, i + dist - 1));
        }
        return min;
    }

    static long calc(int lo, int hi) {
        long ret = 0;
        long big = arr[hi] * (hi - lo + 1);
        long small = 0;
        for (int i = lo; i <= hi; i++) {
            small += arr[i];
        }
        ret = big - small;
        return ret;
    }
}
반응형

'알고리즘 > [ Baekjoon ]' 카테고리의 다른 글

[ BOJ ][JAVA][14950] 정복자  (0) 2021.05.27
[ BOJ ][JAVA][10713] 기차 여행  (0) 2021.05.27
[ BOJ ][JAVA][20495] 수열과 헌팅  (0) 2021.05.26
[ BOJ ][JAVA][16719] ZOAC  (0) 2021.05.26
[ BOJ ][JAVA][1368] 물대기  (0) 2021.05.26