https://www.acmicpc.net/problem/15423
#한국어 번역
설명:
화폐 시스템 S는 실제 또는 가상의 화폐 시스템에서 동전의 값을 나타내는 고유한 양의 정수들의 유한 집합(비어있지 않은 집합)입니다. 예를 들어, 캐나다에서 사용되는 화폐 시스템은 {1, 5, 10, 25, 100, 200}으로, 1은 1센트 동전을 나타내고 200은 2달러(200센트)를 나타냅니다. 우리는 동전이 무한히 많다고 가정하며, S에는 항상 1이 포함된다고 가정합니다. 1이 포함된다는 것은 모든 양의 정수를 동전들의 합으로 표현할 수 있음을 보장합니다.
전 세계의 계산원들은 다음과 같은 문제에 직면합니다: 주어진 화폐 시스템과 고객에게 갚아야 할 양의 정수 금액이 있을 때, 정확히 그 금액을 맞추기 위해 필요한 최소 동전의 수는 얼마인가? 예를 들어, 캐나다의 계산원이 고객에게 83센트를 갚아야 한다고 가정합시다. 한 가지 가능한 방법은 25+25+10+10+10+1+1+1, 즉 8개의 동전을 주는 것이지만, 이는 최적의 방법이 아닙니다. 계산원은 25 + 25 + 25 + 5 + 1 + 1 + 1로 7개의 동전을 줄 수 있으며, 이 방법이 최적입니다.
다행히도, 캐나다 화폐 시스템은 탐욕 알고리즘이 항상 최적의 해를 제공하는 성질을 가지고 있습니다. 탐욕 알고리즘은 반복적으로 남은 금액보다 작거나 같은 가장 큰 값을 가진 동전을 선택하는 방식으로 진행합니다. 탐욕 알고리즘이 항상 최적의 해를 제공하는 화폐 시스템을 '정식(canonical)' 화폐 시스템이라고 부릅니다.
여러분의 도전 과제는 다음과 같습니다: 주어진 화폐 시스템 S = {c1, c2, ..., cn}에 대해 S가 정식인지 비정식(non-canonical)인지 판단하는 것입니다. 만약 S가 비정식이라면, 최소한 하나의 반례가 존재하며, 이는 탐욕 알고리즘으로 구한 동전 개수가 최적의 동전 개수보다 많아지는 양의 정수 x입니다.
입력:
입력은 하나의 경우로 구성됩니다. 첫 번째 줄에는 동전 종류의 수 n (2 ≤ n ≤ 100)이 주어집니다. 다음 줄에는 n개의 동전 값 c1 c2 ... cn이 공백으로 구분되어 주어지며, c1 = 1이고 c1 < c2 < ... < cn ≤ 10^6입니다.
출력:
화폐 시스템이 정식이면 "canonical"을, 비정식이면 "non-canonical"을 출력하세요.
#문제 간단 정리
그리디 + dp 를 구현하라
#문제 해결 방법
우선 딱 보면 알수 있겟지만
그리디를 구현하고
dp를 통한 최적해를 통해서
둘을 비교하면 얻을 수 있다는 것을 알 수 있다.
그리디는 쉽게 구현할 수 있으니 넘어가고
dp는 배낭문제와 같은데
dp[i]는 금액 i을 만들기 위한 최소 동전 수
dp[i] = min(dp[i], dp[i - coins[j]] + 1);
의 점화식을 갖는다
그리고
확인하는 최대 금액이 어째서
int max_x = coins[n-2] + coins[n-1];
인지가 중요한거 같은데
지문에
An example of a non-canonical coin system is {1, 3, 4}, for which 6 is a counterexample, since the greedy algorithm yields 4 + 1 + 1 (3 coins), but an optimal solution is 3 + 3 (2 coins). A useful fact (due to Dexter Kozen and Shmuel Zaks) is that if S is noncanonical, then the smallest counterexample is less than the sum of the two largest denominations.
라고 나와있는데
"예를 들어, {1, 3, 4}라는 비정식(non-canonical) 화폐 시스템에서는 6이 반례(counterexample)가 됩니다. 탐욕 알고리즘을 사용하면 4 + 1 + 1, 즉 3개의 동전이 사용되지만, 최적의 해법은 3 + 3으로 2개의 동전만 필요합니다. Dexter Kozen과 Shmuel Zaks에 따르면, 화폐 시스템이 비정식이라면 가장 작은 반례는 두 개의 가장 큰 동전 값의 합보다 작다는 유용한 사실이 있습니다."
라고 잘 나와잇다.
#전체 코드
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
vector<int> coins;
int greedy(int value,vector<int> &coins) {
int count = 0;
int remaining = value;
for (int i = coins.size() - 1; i >= 0; i--) {
if (coins[i] <= remaining) {
int num = remaining / coins[i];
count += num;
remaining -= coins[i] * num;
}
if (remaining == 0) {
break;
}
}
return count;
}
int main() {
int n;
cin >> n;
coins.resize(n);
for (int i = 0; i < n; i++) {
cin >> coins[i];
}
sort(coins.begin(), coins.end());
int max_x = coins[n - 2] + coins[n - 1];
vector<int> dp(max_x + 1, max_x + 1);
dp[0] = 0;
for (int i = 1; i <= max_x; i++) {
for (int j = 0; j < n; j++) {
if (coins[j] <= i) {
dp[i] = min(dp[i], dp[i - coins[j]] + 1);
}
else {
break;
}
}
}
bool isCanonical = true;
for (int x = 1; x <= max_x; ++x) {
int greedy_count = greedy(x, coins);
if (greedy_count > dp[x]) {
isCanonical = false;
break;
}
}
if (isCanonical) {
cout << "canonical" << '\n';
}
else {
cout << "non-canonical" << '\n';
}
return 0;
}
'[백준] > C++' 카테고리의 다른 글
백준 15812번 침략자 진아 [C++] (0) | 2024.10.02 |
---|---|
백준 24595번 Rise and Fall [C++] (0) | 2024.10.01 |
백준 16405번 Birthday Boy [C++] (1) | 2024.09.21 |
백준 4040번 Polar Bear [C++] (1) | 2024.09.21 |
백준 31747번 점호 [C++] (0) | 2024.09.16 |