Problem Solving/BOJ(백준)
[BOJ]백준 17951번: 흩날리는 시험지 속에서 내 평점이 느껴진거야 / Parametric Search
이진2
2021. 4. 17. 15:40
17951번: 흩날리는 시험지 속에서 내 평점이 느껴진거야
시험지를 12, 7, 19, 20과 17, 14, 9, 10 으로 나누면 맞은 문제 개수의 합의 최소는 50이다.
www.acmicpc.net
스터디할 때 풀었던 문제인데 왜 답이 right일까?를 고민하다가 이제 이유를 알아냈다
이진탐색 및 매개변수 탐색의 진행에는 loop-invarient라는 성질이 적용된다.
loop를 지속하게 하는 절대적인 조건이므로, loop가 시작되기 전 & loop 중 & loop 이후에도 정해진 loop-invarient는 유지되어야 한다.
이 문제(parametric search)에서는 parameter를 "각각의 그룹에 속한 시험지 점수의 합 S 중 최소값을 결정하는 기준 점수"로 설정했다
그래서 loop가 반복될 동안 두 가지 조건은 항상 성립한다
- l은 r보다 작다
- 그룹 기준 점수를 l로 했을 때 그룹의 개수는 k보다 크거나 같고, r로 했을 때 그룹의 개수는 k보다 작다
반복문이 종료될 때 까지 위의 두 가지 성질은 항상 만족되고, 종료된 순간 l과 r의 차이를 최소화 하는 범위를 찾아낸 것임이 확인된다.
따라서 우리가 찾는 답은 반복이 종료된 뒤의 l(r-1)이 되어야 한다.
#include <bits/stdc++.h>
#define MAX 2147483647
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, k; cin >> n >> k;
int ans = 0;
int l = -1;
int r = 0;
vector<int> arr(n);
for (int i = 0; i < n; i++)cin >> arr[i];
for (int i = 0; i < n; i++)r += arr[i]; //총 합을 최댓값(r)으로 설정
r++;
//초기 l과 r값 = left 경계값과 right 경계값
// parameter = k개의 그룹으로 나누었을 때, 그룹의 점수 합의 최소값
// expected value = 그룹의 점수 합의 최소값을 최대로 하는 기준 점수 x
// ✔그룹 기준 점수를 i+1로 했을 때 그룹의 개수 < k <= 그룹 기준 점수를 i로 했을 때 그룹의 개수
// loop invariant에 의해 다음의 두 성질이 loop동안 유지됨
// 1. l < r
// 2. NumOfGroupsByScore(r) < k <= NumOfGroupsByScore(l)
while (l+1 < r) {
int cnt = 0;
int sum = 0;
int mid = (l + r) / 2;
for (int i = 0; i < n; i++) {
sum += arr[i]; //각 시험지의 점수를 더하고
if (sum >= mid) { //설정한 기준 점수보다 sum값이 커지면 그룹을 분리함
sum = 0;
cnt++; //그룹 개수 카운트
}
}
if (cnt >= k) { //카운트한 그룹 개수가 k보다 크거나 같으면 기준 점수를 높여서 그룹의 개수를 더 적게 생성하는 범위로 좁힘
l = mid;
}
if (cnt < k) { //카운트한 그룹 개수가 k보다 작으면 기준 점수를 낮춰서 그룹의 개수를 더 많이 생성하는 범위로 좁힘
r = mid;
}
}
// loop가 종료되었을 때 ✔를 만족하는 가장 큰 i를 찾아야 하므로 원하는 답이 l(r-1)임을 알 수 있다
cout << l;
return 0;
}
아직은 문제를 풀면서 동시에 검증할 수는 없겠지만 loop invarient 조건을 설정해주고 정답을 확인하는 용도로 활용할 수 있을 것 같다