문제 링크
https://www.acmicpc.net/problem/19942
문제
식재료 N개 중에서 몇 개를 선택해서 이들의 영양분(단백질, 탄수화물, 지방, 비타민)이 일정 이상이 되어야 한다. 아래 표에 제시된 6가지의 식재료 중에서 몇 개를 선택해서 이들의 영양분의 각각 합이 최소 100, 70, 90, 10가 되도록 하는 경우를 생각해보자. 이 경우 모든 재료를 선택하면 쉽게 해결되지만, 우리는 조건을 만족시키면서도 비용이 최소가 되는 선택을 하려고 한다.
재료 | 단백질 | 지방 | 탄수화물 | 비타민 | 가격 |
1 | 30 | 55 | 10 | 8 | 100 |
2 | 60 | 10 | 10 | 2 | 70 |
3 | 10 | 80 | 50 | 0 | 50 |
4 | 40 | 30 | 30 | 8 | 60 |
5 | 60 | 10 | 70 | 2 | 120 |
6 | 20 | 70 | 50 | 4 | 40 |
예를 들어, 식재료 1, 3, 5를 선택하면 영양분은 100, 145, 130, 10으로 조건을 만족하지만 가격은 270이 된다. 대신 2, 3, 4를 선택하면 영양분의 합은 110, 130, 90, 10, 비용은 180이 되므로, 앞의 방법보다는 더 나은 선택이 된다.
입력으로 식재료 표가 주어졌을 때, 최저 영양소 기준을 만족하는 최소 비용의 식재료 집합을 찾아야 한다.
입력
첫 줄에 식재료의 개수 N이 주어진다.
다음 줄에는 단백질, 지방, 탄수화물, 비타민의 최소 영양성분을 나타내는 정수 mp, mf, ms, mv가 주어진다.
이어지는 N개의 각 줄에는 i번째 식재료의 단백질, 지방, 탄수화물, 비타민과 가격이 5개의 정수 pi, fi, si, vi, ci와 같이 주어진다. 식재료의 번호는 1부터 시작한다.
출력
첫 번째 줄에 최소 비용을 출력하고, 두 번째 줄에 조건을 만족하는 최소 비용 식재료의 번호를 공백으로 구분해 오름차순으로 한 줄에 출력한다. 같은 비용의 집합이 하나 이상이면 사전 순으로 가장 빠른 것을 출력한다.
조건을 만족하는 답이 없다면 -1을 출력하고, 둘째 줄에 아무것도 출력하지 않는다.
제한
- 3 ≤ N ≤ 15
- 0 ≤ mp, mf, ms, mv ≤ 500
- mp + mf + ms + mv > 0
- 0 ≤ pi, fi, si, vi, ci ≤ 500
예제 입력 1
6
100 70 90 10
30 55 10 8 100
60 10 10 2 70
10 80 50 0 50
40 30 30 8 60
60 10 70 2 120
20 70 50 4 4
예제 출력 1
134
2 4 6
알고리즘 분류
- 백트래킹
풀이
백트래킹을 사용하여 식재료를 고를 수 있는 모든 경우의 수를 탐색하면서 최소의 비용을 구한다.
동일한 최소의 비용을 지불할 수 있는 여러 경우의 수가 있다면 정렬했을 때 사전 순으로 가장 앞서는 경우의 수를 출력하면 된다.
조건을 만족하는 경우의 수가 존재하지 않는다면 -1만 출력한다.
코드
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#define MAX 16
#define INF 2e9
#define FASTIO cin.tie(0); cout.tie(0);
using namespace std;
struct INFO {
int P, F, S, V, C;
};
int N, MP, MF, MS, MV;
vector<INFO> Vec;
bool visited[MAX];
int Answer = INF;
vector<int> Which;
void input() {
cin >> N;
cin >> MP >> MF >> MS >> MV;
for (int i = 0; i < N; i++) {
int P, F, S, V, C;
cin >> P >> F >> S >> V >> C;
Vec.push_back({ P,F,S,V,C });
}
}
void checking(int P, int F, int S, int V, int C) {
if ((P >= MP) && (F >= MF) && (S >= MS) && (V >= MV)) {
if (Answer == INF) {
Answer = C;
for (int i = 0; i < N; i++) {
if (visited[i]) {
Which.push_back(i + 1);
}
}
}
else {
if (Answer == C) {
vector<int> Tmp;
for (int i = 0; i < N; i++) {
if (visited[i]) {
Tmp.push_back(i + 1);
}
}
string A = "";
string B = "";
for (int i = 0; i < Tmp.size(); i++) {
int Cur = Tmp[i];
if ((Cur >= 1) && (Cur <= 9)) {
A += to_string(Cur);
}
else {
A += (char)(55 + Cur);
}
}
for (int i = 0; i < Which.size(); i++) {
int Cur = Which[i];
if ((Cur >= 1) && (Cur <= 9)) {
B += to_string(Cur);
}
else {
B += (char)(55 + Cur);
}
}
if (A < B) {
Answer = C;
Which.clear();
Which = Tmp;
}
}
else if (Answer > C) {
Answer = C;
Which.clear();
for (int i = 0; i < N; i++) {
if (visited[i]) {
Which.push_back(i + 1);
}
}
}
}
}
}
void DFS(int Depth, int CurP, int CurF, int CurS, int CurV, int CurC) {
if (Depth == N) {
checking(CurP, CurF, CurS, CurV, CurC);
return;
}
// 현재 식재료를 선택
visited[Depth] = true;
DFS(Depth + 1, CurP + Vec[Depth].P, CurF + Vec[Depth].F, CurS + Vec[Depth].S, CurV + Vec[Depth].V, CurC + Vec[Depth].C);
// 선택 안 함
visited[Depth] = false;
DFS(Depth + 1, CurP, CurF, CurS, CurV, CurC);
}
void find_Answer() {
if (Answer == INF) {
cout << "-1\n";
}
else {
cout << Answer << "\n";
sort(Which.begin(), Which.end());
for (int i = 0; i < Which.size(); i++) {
cout << Which[i] << " ";
}
cout << "\n";
}
}
int main() {
FASTIO
input();
DFS(0, 0, 0, 0, 0, 0);
find_Answer();
return 0;
}
'BOJ > Gold' 카테고리의 다른 글
[BOJ/Gold 5] 백준 18428 감시 피하기(C++) (0) | 2022.12.25 |
---|---|
[BOJ/Gold 5] 백준 20208 진우의 민트초코우유(C++) (0) | 2022.12.24 |
[BOJ/Gold 4] 백준 6135 Cow Hurdles(C++) (0) | 2022.12.07 |
[BOJ/Gold 4] 백준 9870 Vacation Planning(C++) (1) | 2022.12.07 |
[BOJ/Gold 5] 백준 14588 Line Friends (Small)(C++) (0) | 2022.12.05 |