박스 채우기
시간 제한 | 메모리 제한 | 제출 | 정답 | 맞힌 사람 | 정답 비율 |
---|---|---|---|---|---|
2 초 | 128 MB | 4718 | 1271 | 909 | 27.224% |
문제
세준이는 length × width × height 크기의 박스를 가지고 있다.
그리고 세준이는 이 박스를 큐브를 이용해서 채우려고 한다.
큐브는 정육면체 모양이며, 한 변의 길이는 2의 제곱꼴이다.
(1×1×1, 2×2×2, 4×4×4, 8×8×8, …)
세준이가 가지고 있는 박스의 종류와 큐브의 종류와 개수가 주어졌을 때,
박스를 채우는데 필요한 큐브의 최소 개수를 출력하는 프로그램을 작성하시오.
입력
첫째 줄에 세 자연수 length width height가 주어진다.
둘째 줄에 세준이가 가지고 있는 큐브의 종류의 개수 N이 주어진다.
셋째 줄부터 총 N개의 줄에 큐브의 종류 Ai와 개수 Bi가 i가 증가하는 순서대로 주어진다.
큐브의 종류는 한 변의 길이를 나타낼 때 쓰는 2i에서 i이다.
출력
첫째 줄에 필요한 큐브의 개수의 최솟값을 출력한다.
만약 채울 수 없다면 -1을 출력한다.
제한
- 1 ≤ length, width, height ≤ 106
- 1 ≤ n ≤ 20
- 0 ≤ Ai < 20
- 0 ≤ Bi ≤ 106
- Ai ≠ Aj (i ≠ j)
예제 입력 1
4 4 8 3 0 10 1 10 2 1
예제 출력 1
9
예제 입력 2
4 4 8 3 0 10 1 10 2 10
예제 출력 2
2
예제 입력 3
10 10 11 1 0 2000
예제 출력 3
1100
예제 입력 4
10 10 11 1 0 1099
예제 출력 4
-1
예제 입력 5
37 42 59 6 0 143821 1 14382 2 1438 3 143 4 14 5 1
예제 출력 5
5061
출처
알고리즘 분류
length × width × height 크기의 박스
이 박스를 큐브로 채우려고 한다.
큐브의 크기는 정육면체 모양이며 한 변의 길이는 2의 제곱 꼴 1, 2, 4, 8…
박스를 채우는데 필요한 큐브의 최소 개수를 찾는 문제
4 4 8 // 박스 length × width × height
3 // 큐브의 개수
0 10 // 변의 길이 : 2^0 10개 넓이 1 10개
1 10 // 변의 길이 : 2^1 10개 넓이 8 10개
2 1 // 변의 길이 : 2^2 1개 넓이 64 1개
넓이 128 64 + 10 + 10 + 10 + 10 + 10 + 10
박스는 부피
큐브는 각 큐브의 한 변의 길이와 개수만 가지고 있으면 될 것 같다
박스가 큐브의 개수와는 상관없이
만들어질 수 있는 경우의 수를 조건과 비교하면서 하면 어떨까???
입력 확인
#include <iostream> #include <set> using namespace std; // 큐브의 변의 길이 순으로 내림차순 정렬 set<int, greater<int>> cubeSet; set<int, greater<int>> ::iterator iter; // 큐브의 개수를 저장 / 인덱스는 2^index int cubeNumber[21] = { 0 }; // 박스의 가 세 높 int length, width, height = 0; // 박스의 부피 int boxArea = 0; // 큐브의 종류 , 큐브의 수 int type, Number = 0; void Initializaion() { cin >> length >> width >> height; boxArea = length * width * height; int N = 0; cin >> N; while (N > 0) { cin >> type >> Number; cubeSet.insert(type); cubeNumber[type] += Number; N--; } } int main() { Initializaion(); cout << "박스의 부피 : " << boxArea << "\n"; for (iter = cubeSet.begin(); iter != cubeSet.end(); iter++) { cout << "큐브 변의 길이 2^ " << *iter << " 박스의 개수 : " << cubeNumber[*iter] << " \n"; } return 0; }
마지막 테스트 케이스에서 망… 함 ㅠ
부피로 접근하는 방법은 잘못된 방법이다
큐브들의 부피가 박스의 부피와 같다고 큐브가 박스 모양일 거라는 착각…
잘못된 로직과 코드 1
#include <iostream> #include <set> #include <cmath> using namespace std; // 큐브의 변의 길이 순으로 내림차순 정렬 set<int, greater<int>> cubeSet; set<int, greater<int>> ::iterator iter; // 큐브의 개수를 저장 / 인덱스는 2^index int cubeNumber[21] = { 0 }; // 박스의 가 세 높 int length, width, height = 0; // 박스의 넓이 int boxArea = 0; // 큐브의 종류 , 큐브의 수 int type, Number = 0; void Initializaion() { cin >> length >> width >> height; boxArea = length * width * height; int N = 0; cin >> N; while (N > 0) { cin >> type >> Number; cubeSet.insert(type); cubeNumber[type] += Number; N--; } } void CubeCheck(int cnt, int value) { if (value == boxArea) { cout << "찾았다 : " << cnt << " boxArea : " << boxArea << " value : " << value << "\n"; for (auto it = cubeSet.begin(); it != cubeSet.end(); it++) { cout << "it : " << *it << " cubeNumber[*it] : " << cubeNumber[*it] << "\n"; } exit(0); } for (auto it = cubeSet.begin(); it != cubeSet.end(); it++) { if (cubeNumber[*it] == 0 || value > boxArea ) { return; } //cout << "cubeNumber[" << *it << "] : " << cubeNumber[*it] << "\n"; cubeNumber[*it]--; //cout << "cnt : " << cnt << " 들어갈 때 : " << value << " "; CubeCheck(cnt + 1, value + pow(pow(2,*it),3)); cubeNumber[*it]++; } cout << -1; exit(0); } int main() { Initializaion(); CubeCheck(0, 0); return 0; }
그렇다면 박스에서 접근해야 한다.
박스를 큐브의 규격을 기준으로 분할하여 그리디 알고리즘을 적용하면서 푸는 것 인가?
잘못된 로직과 코드 2
#include <iostream> #include <set> #include <cmath> #include <algorithm> using namespace std; // 큐브의 변의 길이 순으로 내림차순 정렬 set<int, greater<int>> cubeSet; set<int, greater<int>> ::iterator iter; // 큐브의 개수를 저장 / 인덱스는 2^index int cubeNumber[21] = { 0 }; int cubeNeedNumber[21] = { 0 }; // 박스의 X,Y,Z int BoxXYZ[3] = { 0 }; int temp[3] = { 0 }; // 큐브의 종류 , 큐브의 수 int type, Number = 0; int cubeCNT = 0; void Initializaion() { cin >> BoxXYZ[0] >> BoxXYZ[1] >> BoxXYZ[2]; sort(BoxXYZ, BoxXYZ + (sizeof(BoxXYZ)/sizeof(BoxXYZ[0]))); int N = 0; cin >> N; while (N > 0) { cin >> type >> Number; cubeSet.insert(type); cubeNumber[type] += Number; N--; } } // 가장 큰 X를 기준으로 Z를 채워나감 // 들어오는 값은 X의 길이/Z의 남은 길이/ 몇번 나누었는지 확인(0부터 시작) // 이 함수의 목적은 Z가 0이 될때까지 큐브를 찾는 것 void CubeCheckZ(int N, int leftZ, int divisionCount, int st) { for (;leftZ >= pow(2, N);) { cout << "leftZ : " << leftZ << " pow(2, N) : " << pow(2, N) << "\n"; if (divisionCount == 0 && st == pow(2, N)) { BoxXYZ[2] -= st; st = -1; } if (st == -1) { cubeNeedNumber[N] += pow(4, divisionCount); } else { cubeNeedNumber[N] += pow(4, divisionCount) * (st / pow(2, N)); } leftZ -= pow(2, N); if (leftZ == 0) { return; } } // 남은 Z의 길이보다 pow(2, N)가 더 큰 상황 for (;leftZ < pow(2, N);) { N--; divisionCount++; cout << "남은 Z의 길이보다 작은 박스 값 찾기 : " << " N : " << N << " pow(2, N) : " << pow(2, N) << " leftZ : " << leftZ << "\n"; } CubeCheckZ(N, leftZ, divisionCount, st); } // 박스 전체의 X길이와 Z의 길이 void CubeCheckX(int BoxX, int BoxZ, int st, int boxY) { if (BoxX == 0) { return; } // 처음 할 때 int N = 0; for (int i = 0; i <= 20; i++) { if (boxY < pow(2, i)) { N = i - 1; break; } else if (boxY == pow(2, i)) { N = i; break; } //X의 값에 가장 근접한 2^i 를 구함 if (BoxX < pow(2, i) ) { N = i - 1; cout << "높을때 : " << N ; //cubeNeedNumber[N]++; break; } else if (BoxX == pow(2, i) ) { //cout << "BoxX : " << BoxX << " 같을때" << N; N = i; //cubeNeedNumber[N]++; break; } } if (st <= pow(2,N)) { st = pow(2, N); } // 박스의 Z길이보다 작거나 같은 2^N 의 N / 박스의 Z 길이 BoxX -= pow(2, N); CubeCheckZ(N , BoxZ, 0, st); cout << "BoxX : " << BoxX << "\n"; CubeCheckX(BoxX, BoxZ, st, boxY); } int main() { Initializaion(); for (;BoxXYZ[2] > 0;) { CubeCheckX(BoxXYZ[0], BoxXYZ[1], 0, BoxXYZ[2]); cout << "BoxXYZ[2] : " << BoxXYZ[2] << "\n"; } for (iter = cubeSet.begin(); iter != cubeSet.end(); iter++) { cout << "cubeNeedNumber[" << *iter << "] : " << cubeNeedNumber[*iter] << "\n"; } return 0; }
잘못된 로직과 코드 3
작성하다 보니 내가 왜 이렇게 작성했는지 이해할 수 없다.
#include <iostream> #include <set> #include <cmath> #include <algorithm> using namespace std; set<int, greater<int>> cubeSet; set<int, greater<int>> ::iterator iter; int cubeNumber[21] = { 0 }; int cubeNeedNumber[21] = { 0 }; long long BoxXYZ[3] = { 0 }; int type, Number = 0; void Initializaion() { cin >> BoxXYZ[0] >> BoxXYZ[1] >> BoxXYZ[2]; sort(BoxXYZ, BoxXYZ + (sizeof(BoxXYZ)/sizeof(BoxXYZ[0]))); int N = 0; cin >> N; while (N > 0) { cin >> type >> Number; cubeSet.insert(type); cubeNumber[type] += Number; N--; } } void CubeCheckZ(long long N, long long leftZ, long long divisionCount, long long MaxX, bool firstCheck) { for (;leftZ >= pow(2, N);) { if (MaxX == pow(2, N) || leftZ >= pow(2, N) && firstCheck) { if (N == 0) { cubeNeedNumber[N] += leftZ; leftZ = 0; continue; } cubeNeedNumber[N] += pow(4, divisionCount); cout << "pow(4, divisionCount) : " << pow(4, divisionCount) << " divisionCount : " << divisionCount << " N : "<< N << "\n"; leftZ -= pow(2, N); continue; } if (leftZ >= pow(2, N) && !firstCheck) { if (N == 0) { cubeNeedNumber[N] += leftZ * MaxX / pow(2, N); leftZ = 0; continue; } cubeNeedNumber[N] += MaxX / pow(2, N); cout << "MaxX / pow(2, N) : " << MaxX / pow(2, N) << " " << MaxX << " / " << pow(2, N) << "\n"; leftZ -= pow(2, N); } } if (leftZ == 0) { cout << "leftZ : " << leftZ << "\n"; return; } while(leftZ < pow(2, N)) { N--; divisionCount++; } CubeCheckZ(N, leftZ, divisionCount, MaxX, firstCheck); } void CubeCheckX(long long BoxX, long long BoxZ, long long MaxX, long long boxY, bool firstCheck) { if (BoxX == 0) { BoxXYZ[2] -= MaxX; return; } int N = 0; for (int i = 0; i <= 20; i++) { if (BoxX < pow(2, i) || (BoxXYZ[2] < pow(2, i))) { N = i - 1; BoxX -= pow(2, N); break; } else if (BoxX == pow(2, i) || (BoxXYZ[2] == pow(2, i))) { N = i; BoxX -= pow(2, N); break; } } if (MaxX < pow(2,N)) { MaxX = pow(2, N); } CubeCheckZ(N , BoxZ, 0, MaxX, firstCheck); firstCheck = false; CubeCheckX(BoxX , BoxZ, MaxX, boxY, firstCheck); } int main() { Initializaion(); while (BoxXYZ[2] != 0) { CubeCheckX(BoxXYZ[0], BoxXYZ[1], 0, BoxXYZ[2], true); cout << BoxXYZ[2] << " \n"; } for (iter = cubeSet.begin(); iter != cubeSet.end(); iter++) { cout << "cubeNeedNumber[" << *iter << "] : " << cubeNeedNumber[*iter] << "\n"; } return 0; }
통과된 코드
위와 같이 분할 된 박스를 다시 분할 X, Y, Z 가 0일 때까지
상자의 개수 조건에 부합하지 않으면 체크하고 -1 출력
#include <iostream> #include <map> #include <cmath> using namespace std; //cubeMultimap 은 내림차순으로 정렬된다. multimap<int, int, greater<int>> cubeMultimap; //cubeMultimap 순회를 위한 반복자 multimap<int, int, greater<int>>::iterator iter; int length, width, height; int numberOfCubes, cnt = 0; int tempOne, tempTwo; bool failCheck = false; // 조건을 초기화 해주는 함수 void Initializaion() { cin >> length >> width >> height >> numberOfCubes; while (numberOfCubes > 0) { cin >> tempOne >> tempTwo; cubeMultimap.insert(make_pair(pow(2, tempOne), tempTwo)); numberOfCubes--; } } // 분할 정복을 위한 함수 (X, Y, Z, 반복자) void divisionConquest(int length, int width, int height, multimap<int, int, greater<int>>::iterator iter) { // 만약 X, Y, Z 중에서 0이 되는 값이 있다면 리턴 if (length == 0 || width == 0 || height == 0) return; // 분할정복 로직부분 for (;iter != cubeMultimap.end(); iter++) { if (iter->second != 0 && length >= iter->first && width >= iter->first && height >= iter->first) { iter->second--; cnt++; divisionConquest(iter->first, iter->first, width - (iter->first), iter); divisionConquest(length - iter->first, height, width, iter); divisionConquest(height - iter->first, width, iter->first, iter); return; } } // 만약 큐브의 조건이 만족하지 않는다면 실패 마킹 failCheck = true; return; } int main() { Initializaion(); // cubeMultimap의 가장 높은 값부터 시작 iter = cubeMultimap.begin(); divisionConquest(length, width, height, iter); // 실패 마킹이 true 면 항상 -1 출력 if (failCheck) cout << -1; else cout << cnt; // 아니면 큐브의 개수를 출력한다. return 0; }
분할 정복의 개념과 구현 방법은 몰라서 오래 고민한 문제
재귀 구현 어려웠다.
Hey! Do you know if they make any plugins to help with Search Engine Optimization?
I’m trying to get my website to rank for some targeted keywords but I’m not seeing very good gains.
If you know of any please share. Thanks! I saw similar
art here: Bij nl