📕문제
팀 레드시프트는 대회 준비를 하다가 지루해져서 샌드박스 게임인 ‘마인크래프트’를 켰다. 마인크래프트는 1 × 1 × 1(세로, 가로, 높이) 크기의 블록들로 이루어진 3차원 세계에서 자유롭게 땅을 파거나 집을 지을 수 있는 게임이다.
목재를 충분히 모은 lvalue는 집을 짓기로 하였다. 하지만 고르지 않은 땅에는 집을 지을 수 없기 때문에 땅의 높이를 모두 동일하게 만드는 ‘땅 고르기’ 작업을 해야 한다.
lvalue는 세로 N, 가로 M 크기의 집터를 골랐다. 집터 맨 왼쪽 위의 좌표는 (0, 0)이다. 우리의 목적은 이 집터 내의 땅의 높이를 일정하게 바꾸는 것이다. 우리는 다음과 같은 두 종류의 작업을 할 수 있다.
- 좌표 (i, j)의 가장 위에 있는 블록을 제거하여 인벤토리에 넣는다.
- 인벤토리에서 블록 하나를 꺼내어 좌표 (i, j)의 가장 위에 있는 블록 위에 놓는다.
1번 작업은 2초가 걸리며, 2번 작업은 1초가 걸린다. 밤에는 무서운 몬스터들이 나오기 때문에 최대한 빨리 땅 고르기 작업을 마쳐야 한다. ‘땅 고르기’ 작업에 걸리는 최소 시간과 그 경우 땅의 높이를 출력하시오.
단, 집터 아래에 동굴 등 빈 공간은 존재하지 않으며, 집터 바깥에서 블록을 가져올 수 없다. 또한, 작업을 시작할 때 인벤토리에는 B개의 블록이 들어 있다. 땅의 높이는 256블록을 초과할 수 없으며, 음수가 될 수 없다.
📕입력
첫째 줄에 N, M, B가 주어진다. (1 ≤ M, N ≤ 500, 0 ≤ B ≤ 6.4 × 107)
둘째 줄부터 N개의 줄에 각각 M개의 정수로 땅의 높이가 주어진다. (i + 2) 번째 줄의 (j + 1) 번째 수는 좌표 (i, j)에서의 땅의 높이를 나타낸다. 땅의 높이는 256보다 작거나 같은 자연수 또는 0이다.
📕출력
첫째 줄에 땅을 고르는 데 걸리는 시간과 땅의 높이를 출력하시오. 답이 여러 개 있다면 그중에서 땅의 높이가 가장 높은 것을 출력하시오.
🔎문제 해석
문제를 풀기 전에는 항상 어떻게 하면 깔끔하게 풀 수 있을까를 굉장히 고민을 많이 한다.
고민의 결과는 땅의 높이는 최대 256이기에 모든 경우의 수를 구해도 시간초과가 나지 않을 것 같아서 브루트 포스 알고리즘(약칭 : 막일)을 사용하기로 했다.
n * m 행렬에 대해서 0~256까지의 높이에 대해서 검사를 했다.
우선 해당 지점에서 검사하고자 하는 높이와의 차이를 변수 num에 저장했다.
💡num값은 3가지로 분류될것이다.
- 양수 : 해당 지점과의 차이가 양수라는 뜻은 쌓여있는 블록의 개수를 제거해서 인벤토리에 넣는다.
- 음수 : 해당 지점과의 차이가 음수라는 뜻은 인벤토리에 있는 블록을 꺼내서 땅에 쌓아야 한다.
- 0 : 해당 지점과의 차이가 0은 인벤토리에 들어있는 블록 개수의 변화가 없다.
💡인벤토리에서 꺼내는 블록의 수가 (인벤토리에 넣은 블록의 수 + 기존 인벤토리에 있는 블록의 수) 보다 적어야 한다.
만약 크다면 해당 높이에 대해서는 작업이 이루어질 수 없다!
그 후에 인벤토리에서 꺼낼 때의 작업이 1초가 걸리고, 인벤토리에서 넣을 때의 작업이 2초가 걸리기에 해당 시간을 t에 저장하고 그때의 높이를 저장하고 출력했다.
📃코드
/*
백준 실버2 마인크래프트
*/
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
int n, m, b,height, bigT = INT_MAX;
vector<vector<int>> graph;
int main()
{
cin >> n >> m >> b;
graph.resize(n, vector<int>(m, 0));
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
cin >> graph[i][j];
}
}
for (int k = 0; k < 257; k++) //모든 높이에 대해서 검사함.
{
int addblock = 0; //높이가 모자랄때 쌓는 블록의 개수를 저장함.
int subblock = 0; //높이가 남을때 제거하는 블록의 개수를 저장함.
int t = 0; //현재 높이에 대해서 검사할 때 시간을 저장함.
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
int num = graph[i][j] - k; //해당 지점에서 높이와의 차이를 저장. -> 필요하거나 남는 블록 수
if (num < 0) { //차이가 음수라면 그 차이만큼 땅의 높이를 올리고 addblock을 증가시킴. 시간 : 1초
addblock += num; //올릴때 쓰이는 블록 수
}
else if (num > 0) { //치이가 양수라면 그 차이만큼 땅의 높이를 내리고 subblock을 증가시킴. 시간 : 2초
subblock += num; //내릴떄 쓰이는 블록 수
}
}
}
if (addblock*-1 > b+subblock) { //내가 더해주고 빼준 블록의 수의 합이 내가 들고있는 블록의 수보다 많다면 이것은 잘못된 것.
continue;
}
else { //subblock-addblock>=b
t = addblock*-1 + subblock * 2;
if (t <= bigT) {
bigT = t;
height = k;
}
}
//높이 k에 대해서 모든 addblock과 subblock을 수집햇고.
//만약 높이가 갚을 경우는 ? 시간이 영초겟죠
/*
int num = graph[i][j];
while (num != k)
{
if (num > k) //높을때는 높이 -1 현재블록수 + 1 --> 시간 2초걸림
{
num -= 1;
nowblock += 1;
t += 2;
}
else //낮을때는 높이 +1 블록 -1 --> 시간 1초걸림
{
num += 1;
nowblock -= 1;
t += 1;
}
if (nowblock < 0)
{ //블록의 개수가 0보다 작아진다면 해당 높이를 맞출수는없음.
flag=false;
}
if(flag==false){
break;
}
}
--> 시간 초과 코드 */
}
cout << bigT << " " << height << endl;
}
✔ 느낀 점
- 처음 작성한 코드에서는 시간 초과가 발생했다.
- 해당 지점과의 높이 차이에 대해서 while문을 돌렸기 때문이다. (사실 엄청 불필요한 작업이었다. 해당 차이만큼 연산을 곱해주면 되는 것인데...)
- 지금 작성한 코드와 기존 코드와의 알고리즘적으로 큰 차이는 없다!.
'CodingTest > Baekjoon' 카테고리의 다른 글
[백준 20057] 마법사 상어와 토네이도(C++) (0) | 2022.09.15 |
---|---|
[백준 14500] 테트로미노(C++) (0) | 2022.09.14 |
[백준 2281] 데스노트(C++) (0) | 2022.09.08 |
[백준 2156] 포도주 시식(C++) (0) | 2022.09.06 |
[백준 1309] 동물원(C++) (0) | 2022.09.04 |