Skip to content

Conversation

@MuchanKim
Copy link
Collaborator

@MuchanKim MuchanKim commented May 21, 2025

🔗 문제 링크

흩날리는 시험지 속에서 내 평점이 느껴진거야

image

다들 챌린지 안녕하신가요?? 시간이 없네요!!!!!!! ㅋㅋㅋㅋ ㅇㅂㅇ 그렇지만 난이도 쉬운 것은 선택하고 싶지 않았습니다^^

✔️ 소요된 시간

2시간 + @
하... 쉬워보였는데 아니었다...

✨ 수도 코드

문제 쉽게 이해하기(드든) 아래에 수도 코드 있어요.

  1. 시험지를 현재 순서 그대로 K개의 그룹으로 나눠야 함
  2. 각 그룹의 점수는 그 그룹에 속한 시험지들의 점수 합임
  3. 모든 그룹의 점수 중 가장 작은 값이 '가장 큰' 경우에서 그 점수를 출력하면 됨

풀이

오래 붙잡다가 블로그 참고해서 풀었어요. 다른분들 풀이가 궁금해지는 문제입니다!
기존에 제가 시도한 방법은 정직하게, 그룹을 나누고 최솟값을 계속 구해줘서 최댓값을 구하는 방식으로 생각했었습니다. 근데 이게 아니었지요.
결국 블로그 풀이를 보니, 기준 점수를 두고 합이 기준점수를 통과하면 그룹으로 묶어주는 방식을 쓰더라고요.
사실이게 잘 안와닿았는데 코드 흐름을 파악하니 이해가 되었습니다.

자 그럼 풀이 가입시더.

  1. 전체적 흐름 이해하기
    핵심은 "기준 점수"를 잡고 이분 탐색으로 최적의 값을 찾아가는 것.

테스트케이스 예시에서 그룹수만 바꿨을 때로 보시죠. (테케 그룹수가 2여서 헷갈림)
scores = [12, 7, 19, 20, 17, 14, 9, 10]
group = 3

"모든 그룹이 35점 이상이 되도록 나눌 수 있나?" 확인해보죠.

  1. 앞에서부터 더해가기:
    12+7+19 = 38 (35점 넘음! 첫 그룹 완성)
    20+17 = 37 (35점 넘음! 둘째 그룹 완성)
    14+9+10 = 33 (35점 못넘음... 실패!)

  2. 결론: 35점은 불가능, 더 낮춰서 시도해보자

이런 식으로 계속 "기준 점수"를 바꿔가면서 "이 점수로 K개 그룹을 만들 수 있나?" 확인하고 가능하면 더 높은 점수를, 불가능하면 더 낮은 점수를 시도하는 겁니다.

  • 높다 → 더 낮춰서 시도
  • 낮다 → 더 높여서 시도
  • 가능한 점수 중 최댓값이 정답.

제가 뭔말하는지 헷갈리시면 아래에 파라메트릭 서치 한번 읽어보시는거 추천!!

📚 새롭게 알게된 내용

파라메트릭 서치(Parametric Search)를 아시나요?

해당 문제 풀이 방법이 파라메트릭 서치를 활용한 방법이었어요. 전 이 개념을 처음 알았는데 앞으로 이런 최소/최대 문제를 푸는 데 도움이 될 것 같다는 생각을 했네요!

시간이 바쁜 여러분을 위해 정말 이해가 잘되는 예시 하나를 알려드릴게요.(아래 글에서 가져옴)
정육점에서 고기 200g을 잴 때를 생각해보면, 보통 두 가지 방법이 있을 수 있습니다.
첫 번째는 고기의 밀도나 지방 비율 같은걸 다 계산해서 정확히 200g이 되는 부분을 찾아 자르는 방법. 근데 이건 너무 복잡하고 현실적으로 불가능합니다.
그래서 우리가 실제로 하는 방법은 "이 정도면 200g 정도 되려나?" 하면서 대충 잘라서 저울에 올려보고, 무게가 더 나가면 조금 덜어내고, 부족하면 더 올려놓고... 이런 식으로 찾아갑니다. 이거시 바로 파라메트릭 서치.. 인 것.
어려운 문제를 "이 정도면 될까? 말까?"라는 간단한 결정 문제로 바꿔서 해결하는 것입니다. ㅎㅎ

https://ialy1595.github.io/post/parametric-search/

Comment on lines 10 to 12
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 right를 reduce값으로 해줬어요! 최대한 정답에 가깝게 설계해야할 것 같아서요!

 var right = arr.reduce(0) {
        $0 + $1
    }

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어.. 이 부분이 어디 말씀하시는거지?? 하고 봤는데
수정된 코드였꾼요!

var end = scores.reduce(0, +)

수정된 코드 좋네요 저도 이렇게 right를 줬어요!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

결국 그냥 answer를 초기화 해주면 마지막에 초기화된게 정답이군요.
저는 배열에 담아서 max값을 return 해줬어요

 if cnt < K {
            right = mid - 1

        } else {
            standardArr.append(mid)
            left = mid + 1
        }
    }
    return standardArr.max()!
    ```

Copy link
Collaborator

@bishoe01 bishoe01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 분류 이분탐색보고 또 left right써서 풀면 되겠다.. 싶었는데, 나눌 수 있는 그룹이 2개가 아니더라구요..! 여기서부터 막막해졌습니다..
힌트랑 힌트는 다찾아보고 잘 안풀려서, 답을 보고 조정을 했습니다..!
이 그룹을 구성하는 값으로 이분탐색을 돌리면서 그룹에 넣어주는게 이해가 진짜 잘 안되더라구요.. 별찍어놓고 다음에 다시 풀어보겠습니다...

// https: www.acmicpc.net/problem/17951
import Foundation

// 시험지 최대 점수 20

func solution(_ N: Int, _ K: Int, _ arr: [Int]) -> Int {
    let LEN = arr.count
    var cnt = 0
    var left = 0
    var right = arr.reduce(0) {
        $0 + $1
    }

    var standardArr: [Int] = []
    while left <= right { // 이건 인덱스가 아니라 그룹당 크기 제한
        let mid = (left + right) / 2
//        print("midmid", mid)결국 그냥 answer를 초기화 해주면 마지막에 초기화된게 정답이군요.
        var tmp = 0
        cnt = 0
        for i in 0 ..< LEN {
            tmp += arr[i]
            if tmp >= mid { // 초과하면 이제 초기화
                cnt += 1
//                print("넘음 ! ", tmp)
                tmp = 0
            }
        }

        if cnt < K {
            right = mid - 1

        } else {
            standardArr.append(mid)
            left = mid + 1
        }
    }
    return standardArr.max()!
}

let input = readLine()!.split(separator: " ").map { Int($0)! }
let N = input[0]
let K = input[1]

let arr = readLine()!.split(separator: " ").map { Int($0)! }

print(solution(N, K, arr))

Comment on lines +9 to +19
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로직 분리한거 보기 좋아요 👍

Copy link
Member

@giljihun giljihun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제 이해하기가 정말 어려웠던거같아요.
갠적으로 거의 비문학이었음.

시험지를 K개 그룹으로 나눌거고,
각 그룹의 합 중 최솟값을 찾을건데,

그 최솟값이 같은 그룹이 여러개다?
-> 그 그룹들의 최솟값 중 가장 큰 값으로 시험 점수를 결정.

아니 이거 다시봐도 말이 이해가 진짜 어렵네요.

아무튼 시행착오 + 이해까지 1시간 30분이 걸렸는데
운이 좋게 직전에 풀었던 문제가 킨더의 이분탐색 문제였고,
킨더문제 풀면서 "이분탐색도 종류가 2개네.."하고 킨더한테 얘기를 했거든요 ㅋㅋㅋ
그래서 운이 좋게 로직 설계는 이해가 잘 됐습니다.

/*
 
 1. 시험지를 현재 순서 그대로 K개의 그룹으로 나눈 뒤
 2. 각각의 그룹에서 맞은 문제 개수의 합을 구하여
 3. 그 중 최솟값을 시험 점수로 하기로 하였다.
 4. 현수가 이번 시험에서 받을 수 있는 최대 점수를 계산하는 프로그램을 작성하자.

 현수는 모르는 문제를 아예 풀지 않기 때문에 현수가 푼 문제는 모두 맞았다고 생각할 수 있으며,
 조교는 마음씨가 좋아서 자신이 줄 수 있는 최대한의 점수를 준다.
 => 이게 먼 소리? 무슨 조건이지?

 N - 시험지 수
 K - 시험지를 나눌 그룹의 수 (* 갯수 상관 없어요..)
 x - 0 ~ 20
 
 10만개의 시험지가 가능함.
 브루트포스로 절대 풀 수 없다.
 
 이거 근데 ㅋㅋ 이번주 킨더문제랑 되게 비슷한거같은데
 이분탐색?

 시험지를 K개 그룹으로 나눌거고
 각 그룹의 합 중 최소값을 찾을건데.
 
 그 최솟값이 같은 그룹이 여러개면,
 그 그룹들의 최솟값 중 가장 큰 값으로 시험 점수를 결정해줌.
 
 */

import Foundation

// 입력
guard let firstInputLine = readLine()?
    .split(separator: " "),
    firstInputLine.count == 2 else { exit(0) }

guard let N = Int(firstInputLine[0]),
      let K = Int(firstInputLine[1]) else { exit(0) }

guard let papers = readLine()?
    .split(separator: " ")
    .compactMap({ Int($0) }) else { exit(0) }

var left = 0
var right = papers.reduce(0, +)

var answer = 0

while left <= right {
    let mid = (left + right) / 2
    if isValid(mid) {
        answer = mid
        left = mid + 1
    } else {
        right = mid - 1
    }
}

func isValid(_ targetScore: Int) -> Bool {
    var sum = 0
    var groupCount = 0
    
    for score in papers {
        sum += score
        if sum >= targetScore {
            groupCount += 1
            sum = 0
        }
    }
    return groupCount >= K
}

print(answer)

로직을 정리하면,

  1. 이분 탐색 범위를 0 ~ 시험지의 총합으로 잡는다. (왜냐면 무조건 이 사이에 있을 수 밖에 없잖아.)
  2. 거기서 mid값을 기준으로 잡자.
  3. 그 mid값으로 k개 이상의 그룹을 만들 수 있는지 확인해봐.
  4. 그룹은, 합이 mid 이상이 될 때마다 하나로 간주해.
  5. 만들 수 있는 그룹 개수가 K개 이상 -> 유효!, 더 높은 점수를 탐색해야함 (left = mid + 1)
  6. 그룹 개수가 부족해? -? 점수가 너무 높음!, 더 낮은 잠수를 탐색해야함 (right = mid - 1)
  7. 이렇게 가능한 점수 중 최댓값을 구해.

쬐에끔 더 쉬운 이해..

isValid(mid) = K개 이상 그룹 만들 수 있음?

→ YES → 가능한 점수니까 더 큰 점수(mid+1) 도전
→ NO → 너무 높은 점수였으니 줄여야 함 (mid-1)

무랑 로직은 거의 동일한거보면 잘 짠거같네요

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

와. ㅋㅋ 문제 조건에서 그냥 K개 그룹으로 나눈다~는 걸

"균등한 개수의 시험지를 갖는 K개 그룹"으로 나눈다고 착각해서
멍청시간 1시간 30분 보내고 왜 도대체 안풀리나 했네요.
처음부터 다시 시작하러 갑니다..

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

왤캐 easy한가 했네..

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋㅋ 저도 그렇게 생각했어요... PR에 설명한다고 적었는데 부족했네요.. sry,,

Comment on lines 10 to 12
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어.. 이 부분이 어디 말씀하시는거지?? 하고 봤는데
수정된 코드였꾼요!

var end = scores.reduce(0, +)

수정된 코드 좋네요 저도 이렇게 right를 줬어요!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants