포스트

BOJ 2728 오늘은 마가리타 한 잔?

sorohue가 PS하는 블로그

BOJ 2728 오늘은 마가리타 한 잔?

문제 링크입니다.

관찰

가게마다 마가리타가 하나씩 준비되어 있을 때, 주어진 예산을 더 이상 살 수 있는 마가리타가 없을 때까지 써서 살 수 있는 마가리타의 조합의 수를 구해야 합니다.

더 이상 살 수 있는 마가리타가 없다는 것은, 마가리타를 모두 산 뒤 남은 돈이 사지 않은 가장 싼 마가리타보다 작다는 의미가 됩니다. 여기에서 착안해, 마가리타를 사는 방법을 가장 싼 마가리타를 사는 경우와 그렇지 않은 경우로 나누어 생각합시다.

가장 싼 마가리타를 사지 않는다면

이 경우, 나머지 마가리타를 사서 가장 싼 마가리타의 가격보다 적은 돈을 남겨야 합니다. 이는 냅색 DP로 해결할 수 있습니다.

  • D[j] : 가장 싼 마가리타를 제외하고, 정확히 j의 예산을 사용해서 마가리타를 구매하는 방법의 수

총 예산이 K, 가장 싼 마가리타의 가격을 V라고 하면, 가장 싼 마가리타를 사지 않는 방법의 수는 D[K - V+1] 부터 D[K] 까지의 합입니다.

가장 싼 마가리타를 산다면

이 경우, 예산에서 가장 싼 마가리타의 가격을 빼고 가장 싼 마가리타를 버립니다. 그러면 남은 예산으로 남은 마가리타를 사는, 그러니까 직전과 값만 다르고 똑같은 상황이 됩니다.

이를 일반화하면 마가리타를 가격 순으로 정렬했을 때, 앞에서부터 순서대로 t개의 마가리타를 모두 사고, t+1 번째 마가리타는 사지 않는 경우를 모두 더해 답을 구할 수 있습니다.

어느 마가리타도 살 수 없는 경우와 모든 마가리타를 살 수 있는 경우를 주의해서 처리하면 총 시간 복잡도 테스트 케이스 당 $\mathcal{O}(N^2K)$에 문제를 해결할 수 있습니다.

코드

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
32
33
34
35
36
37
38
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
ll ans, a[33], d[1234];
int main(){
	cin.tie(0);cout.tie(0);ios::sync_with_stdio(false);
	int T; cin >> T; while(T--){
		ans = 0;
		int n, k; cin >> n >> k;
		ll s = 0;
		for(int i = 1; i <= n; i++){
			cin >> a[i];
			s += a[i];
		}
		if(s <= k){
			cout << "1\n";
			continue;
		}
		sort(a+1, a+n+1);
		if(a[1] > k){
			cout << "0\n";
			continue;
		}
		for(int ex = 1; ex <= n; ex++){
			memset(d, 0, sizeof(d));
			d[0] = 1;
			for(int i = ex+1; i <= n; i++){
				for(int j = k; j >= a[i]; j--){
					d[j] += d[j-a[i]];
				}
			}
			for(int j = k; j > max(-1LL, k-a[ex]); j--) ans += d[j];
			k -= a[ex];
			if(k < 0) break;
		}
		cout << ans << '\n';
	}
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.