[백준]2512 예산

김희윤·2023년 3월 27일
0

이 문제는 이분탐색으로 푸는게 국룰이지만, 이분탐색이 뭔지 몰라 그냥 풀었다.

문제

국가의 역할 중 하나는 여러 지방의 예산요청을 심사하여 국가의 예산을 분배하는 것이다. 국가예산의 총액은 미리 정해져 있어서 모든 예산요청을 배정해 주기는 어려울 수도 있다. 그래서 정해진 총액 이하에서 가능한 한 최대의 총 예산을 다음과 같은 방법으로 배정한다.

모든 요청이 배정될 수 있는 경우에는 요청한 금액을 그대로 배정한다.
모든 요청이 배정될 수 없는 경우에는 특정한 정수 상한액을 계산하여 그 이상인 예산요청에는 모두 상한액을 배정한다. 상한액 이하의 예산요청에 대해서는 요청한 금액을 그대로 배정한다.

예를 들어, 전체 국가예산이 485이고 4개 지방의 예산요청이 각각 120, 110, 140, 150이라고 하자. 이 경우, 상한액을 127로 잡으면, 위의 요청들에 대해서 각각 120, 110, 127, 127을 배정하고 그 합이 484로 가능한 최대가 된다.

여러 지방의 예산요청과 국가예산의 총액이 주어졌을 때, 위의 조건을 모두 만족하도록 예산을 배정하는 프로그램을 작성하시오.

입력
첫째 줄에는 지방의 수를 의미하는 정수 N이 주어진다. N은 3 이상 10,000 이하이다. 다음 줄에는 각 지방의 예산요청을 표현하는 N개의 정수가 빈칸을 사이에 두고 주어진다. 이 값들은 모두 1 이상 100,000 이하이다. 그 다음 줄에는 총 예산을 나타내는 정수 M이 주어진다. M은 N 이상 1,000,000,000 이하이다.

출력
첫째 줄에는 배정된 예산들 중 최댓값인 정수를 출력한다.

문제 간단 설명

N개의 도시가 "우리 도시 예산 이만큼 주세요!"라고 말한다. 그리고 우리나라의 전체 예산을 관리하는 사람이다. 각 도시의 요구예산을 모두 배부할 수 있는지 없는지 확인하여, 총예산이 각 도시의 요구 예산의 합보다 크거나 같으면 요구를 들어주고, 그게 아니라면 예산을 적절하게 분배해야 한다.

예산 분배 방법

예를 들어 4개의 도시에서 각각 120, 110, 140, 150을 달라고 한다. 각 도시의 요구예산의 총합은 520이다. 그러나 우리나라의 전체 예산은 485만원 밖에 없다. 따라서 나는 하나의 도시 당 부여할 수 있는 예산 최대값을 정하고자 한다. 예를 들어 내가 최대값을 127만원으로 정했다면, 120만원을 요구한 도시에게는 120만원을 그대로 주지만, 127만원 이상을 요구한 도시에게는 120만원만 줄 것이다. 이렇게 되면 각 도시마다 120, 110, 127, 127을 줄 수 있다. 왜냐하면 이렇게 주었을 때의 총합이 484만원으로 우리나라 전체 예산인 485만원을 넘지 않기 때문이다.

예산 분배 이후 우리나라의 남은 전체 예산이 최소값이 되도록 하기 위해 도시상 예산 분배 최대값을 126,125,124... 아닌 127만원으로 하는 것이다.

따라서 문제는 다른 case가 주어졌을 때에도 예산 분배 최대값을 찾는 것이다.

사고의 과정

  1. 각 도시가 요구한 예산의 총합이 우리나라 전체 예산보다 작거나 같다면 그대로 준다. 이때 예산 분배 최대값은 도시 중에 가장 높은 가격을 부른 도시의 예산일 것이다. 이를 쉽게 찾기 위해 각 도시별 예산 리스트를 오름차순으로 정렬하고 가장 마지막 예산을 출력한다.
  2. case 1이 아니라면 각 도시마다 1만원부터 최대 어디까지 줄 수 있는지를 조사해야 한다. 1부터 하나하나 다 찾는 것은 매우 비효율적이기 때문에 case를 나눌 것이다
  3. 만약 예산 요구가 120, 110, 140, 150 같이 주어진다면, 가장 작은 요구예산인 110만원을 본다.110만원*4 가 우리나라 총 예산보다 크다면, 최대 예산은 110만원 이하가 되야 한다.
  4. 만약 110만원*4 가 우리나라 총 예산보다 작다면, 최대 예산은 110만원보다 크다. 이때부터 이분탐색(전체중에 절반을 구해서, 절반의 절반을 반복함 - 예를 들어 나이가 1~100살 사이라 할 때, 먼저 50살인지 물어보고 up이라고 답하면 75살이세요? 라고 물어보는거임)을 할 수 있지만, 나는 이걸 코드로 하기 귀찮아서 오히려 더 복잡한 방법으로 풀어버림.
  5. 110만원보다 크다고 했으니 120만원을 조사, 140만원을 조사.. 이렇게 차례대로 하다보면 우리나라 총 예산보다 작은 값이 나올거임. 그럼 그 전의 값을 시작으로 이제부터 차례대로 조사함. 예를 들어 120만원이 최대일 경우 전체 예산보다 작은데, 140만원이 최대일 경우 전체 예산을 초과한다면, 예산 최대 허용 값은 120만원 이상 140만원 이하일거임. 그럼 for문 돌려서 전체탐색하면 끝!.

코드

n = int(input())
arr = map(int, input().split())
sorted_arr = sorted(arr)
max = int(input())
if(sum(sorted_arr)<=max):
  print(sorted_arr[-1])
elif sorted_arr[0]*n > max:
    print(int(max/n))
else:
  standardIndex = 0
  for idx, val in enumerate(sorted_arr):
    tmp_list = sorted_arr[:idx] + [val if x > val else x for x in sorted_arr[idx:]]
    if sum(tmp_list)>max:
      break
    standardIndex = idx
  for standardNumber in range(sorted_arr[standardIndex], sorted_arr[-1]):
    tmp_list = sorted_arr[:standardIndex] + [standardNumber if x > standardNumber else x for x in sorted_arr[standardIndex:]]
    if sum(tmp_list)>max:
      print(standardNumber-1)
      break

다른 사람의 효율적인 코드

참고, 다른 사람의 줜나 효율적인 파이썬 코드 - 이분탐색 이용

import sys
input = sys.stdin.readline
n = int(input())
s = list(map(int, input().split()))
m = int(input())
low, high = 0, max(s)
while low <= high:
    mid = (low + high) // 2
    num = 0
    for i in s:
        if i >= mid: num += mid
        else: num += i
    if num <= m: low = mid + 1
    else: high = mid - 1
print(high)

이 코드는 이분 탐색을 사용하여 예산 내에서 가능한 한 최대의 도시 건설 비용을 찾는 것을 목적으로 합니다.

먼저, 첫 번째 입력값으로 도시의 개수 N을 입력받고, 두 번째 입력값으로 각 도시의 건설 비용을 입력받습니다. 세 번째 입력값으로는 총 예산 budgets을 입력받습니다.

그 다음, 시작점을 0으로, 끝점을 각 도시 중 가장 높은 건설 비용으로 초기화합니다. 이후 이분 탐색을 수행합니다.

이분 탐색을 수행하는 while문에서, 현재 중앙값(mid)을 구한 후, 각 도시의 건설 비용과 mid를 비교하여 최종 지출 양(total)을 계산합니다. total이 budgets보다 작거나 같다면, 최대 지출 양을 더 늘리기 위해 mid를 늘리기 위해 start를 mid+1로 갱신합니다. total이 budgets보다 크다면, 지출 양을 줄이기 위해 mid를 줄이기 위해 end를 mid-1로 갱신합니다.

이렇게 이분 탐색을 수행하면, 예산 내에서 가능한 한 최대의 도시 건설 비용을 찾을 수 있습니다. 마지막으로, 이분 탐색이 끝난 후에는 end값이 정답이 됩니다. 이는 budgets 내에서 가능한 한 최대의 도시 건설 비용을 의미합니다.

profile
탑 개발자를 향해 오늘도 정진한다.

0개의 댓글