
팀 레드시프트는 대회 준비를 하다가 지루해져서 샌드박스 게임인 ‘마인크래프트’를 켰다. 마인크래프트는 1 × 1 × 1(세로, 가로, 높이) 크기의 블록들로 이루어진 3차원 세계에서 자유롭게 땅을 파거나 집을 지을 수 있는 게임이다.
목재를 충분히 모은 lvalue는 집을 짓기로 하였다. 하지만 고르지 않은 땅에는 집을 지을 수 없기 때문에 땅의 높이를 모두 동일하게 만드는 ‘땅 고르기’ 작업을 해야 한다.
lvalue는 세로 N, 가로 M 크기의 집터를 골랐다. 집터 맨 왼쪽 위의 좌표는 (0, 0)이다. 우리의 목적은 이 집터 내의 땅의 높이를 일정하게 바꾸는 것이다. 우리는 다음과 같은 두 종류의 작업을 할 수 있다.
1번 작업은 2초가 걸리며, 2번 작업은 1초가 걸린다. 밤에는 무서운 몬스터들이 나오기 때문에 최대한 빨리 땅 고르기 작업을 마쳐야 한다. ‘땅 고르기’ 작업에 걸리는 최소 시간과 그 경우 땅의 높이를 출력하시오.
단, 집터 아래에 동굴 등 빈 공간은 존재하지 않으며, 집터 바깥에서 블록을 가져올 수 없다. 또한, 작업을 시작할 때 인벤토리에는 B개의 블록이 들어 있다. 땅의 높이는 256블록을 초과할 수 없으며, 음수가 될 수 없다.
첫째 줄에 N, M, B가 주어진다. (1 ≤ M, N ≤ 500, 0 ≤ B ≤ 6.4 × 10^7)
둘째 줄부터 N개의 줄에 각각 M개의 정수로 땅의 높이가 주어진다. (i + 2)번째 줄의 (j + 1)번째 수는 좌표 (i, j)에서의 땅의 높이를 나타낸다. 땅의 높이는 256보다 작거나 같은 자연수 또는 0이다.
땅 고르기 작업이 완료되기 까지의 최소 시간과 높이를 구하는 문제이다.
해당 문제는 가능한 높이 전체의 시간을 비교하는 전수 검사(Brute Force) 알고리즘을 이용해 해결 할 수 있다.
높이가 0층일 때 걸리는 시간 높이가 1층일 때 걸리는 시간 높이가 2층일 때 걸리는 시간 ... 높이가 256층일 때 걸리는 시간(땅의 높이는 256블록을 초과 할 수 없다)
을 모두 비교하여 최소 시간과 그 때의 높이를 출력하면 된다.
하지만 항상 256층을 모두 돌아보기에는 비효율적이라는 생각이 든다.
3 3 1 1 1 1 1 1 1 1 1 0
위와 같은 입력이 주어졌을 때 집터 바깥에서 블록을 가져올 수 없다.라는 제약 조건으로 인해 해당 입력에서는 최대 층수가 1층으로 어떻게 해도 평평한 2층 이상의 땅을 만들 수 없기 때문에 2층 이상의 경우를 고려할 필요가 없어진다.
하지만 0층 ~ 256층 까지 모두 돌아보는 방법은 2층 ~ 256층 만큼 의미 없는 반복이 계속 될 것이다.
이러한 의미 없는 반복을 줄이기 위해서는 현재 가지고 있는 블록(캘 수 있는 블록 + 인벤토리에 있는 블록)을 이용해 최대로 쌓을 수 있는 높이가 몇 층인지 확인해 해당 높이만큼만 반복하면 된다.
(캘 수 있는 블록 + 인벤토리에 있는 블록) / (땅의 가로 * 땅의 세로) 공식(평균 공식)을 이용하면 현재 가지고 있는 블록을 이 용해 쌓을 수 있는 최대 높이를 구할 수 있다.
이후 0층 ~ 256층 까지가 아닌 0층 ~ 최대 높이 까지의 시간만 비교하면 필요한 최소의 반복만으로 해당 문제를 풀어낼 수 있다.
import java.io.*;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] str = br.readLine().split(" ");
// 가로, 세로, 인벤토리 입력
int M = Integer.parseInt(str[0]);
int N = Integer.parseInt(str[1]);
int B = Integer.parseInt(str[2]);
// 땅 높이 입력
int map[][] = new int[M][N];
long sum = 0;
for (int i = 0; i < M; i++) {
str = br.readLine().split(" ");
for (int j = 0; j < N; j++) {
map[i][j] = Integer.parseInt(str[j]);
sum += map[i][j];
}
}
// (캘 수 있는 블록 + 가진 블록) / (가로 * 세로) 공식으로 최대 높이를 구한다
int maxHeight = (int)((sum + B) / (M * N));
int minTime = Integer.MAX_VALUE, targetHeight = 0;
// 최대 높이까지 반복하며 시간을 비교한다
for (int i = 0; i <= maxHeight; i++) {
int time = 0;
for (int j = 0; j < M; j++) {
for (int k = 0; k < N; k++) {
if (map[j][k] > i) time += (map[j][k] - i) * 2;
else if (map[j][k] < i) time += i - map[j][k];
}
}
// 답이 여러 개 있을 경우 가장 높은 층을 저장하기 위해 >= 조건 사용
if (minTime >= time) {
minTime = time;
targetHeight = i;
}
}
System.out.println(minTime + " " + targetHeight);
}
}
#include <iostream>
using namespace std;
int main() {
// 가로, 세로, 인벤토리 입력
int M, N, B;
cin >> M >> N >> B;
// 땅 높이 입력
int map[M][N];
int sum = 0;
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
cin >> map[i][j];
sum += map[i][j];
}
}
// (캘 수 있는 블록 + 가진 블록) / (가로 * 세로) 공식으로 최대 높이를 구한다
int maxHeight = (int)((sum + B) / (M * N));
int minTime = 2147483647, targetHeight = 0;
// 최대 높이까지 반복하며 시간을 비교한다
for (int i = 0; i <= maxHeight; i++) {
int time = 0;
for (int j = 0; j < M; j++) {
for (int k = 0; k < N; k++) {
if (map[j][k] > i) time += (map[j][k] - i) * 2;
else if (map[j][k] < i) time += i - map[j][k];
}
}
// 답이 여러 개 있을 경우 가장 높은 층을 저장하기 위해 >= 조건 사용
if (minTime >= time) {
minTime = time;
targetHeight = i;
}
}
cout << minTime << " " << targetHeight;
return 0;
}
fun main(args: Array<String>) {
// 가로, 세로, 인벤토리 입력
var str = readLine()!!.split(' ')
var M = str[0].toInt()
var N = str[1].toInt()
var B = str[2].toInt()
// 땅 높이 입력
var sum = 0
var map = Array(M,{IntArray(N,{0})})
for(i in 0 until M) {
str = readLine()!!.split(' ')
for(j in 0 until N) {
map[i][j] = str[j].toInt()
sum += map[i][j]
}
}
// (캘 수 있는 블록 + 가진 블록) / (가로 * 세로) 공식으로 최대 높이를 구한다
var maxHeight = (sum + B) / (M * N)
var minTime = Int.MAX_VALUE
var targetHeight = 0
// 최대 높이까지 반복하며 시간을 비교한다
for(i in 0..maxHeight) {
var time = 0
for(j in 0 until M) {
for(k in 0 until N) {
if (map[j][k] > i) time += (map[j][k] - i) * 2
else if (map[j][k] < i) time += i - map[j][k]
}
}
// 답이 여러 개 있을 경우 가장 높은 층을 저장하기 위해 >= 조건 사용
if (minTime >= time) {
minTime = time
targetHeight = i
}
}
println("$minTime $targetHeight")
}
def main():
# 가로, 세로, 인벤토리 입력
M, N, B = map(int, input().split(' '))
arr = [[0 for j in range(N)] for i in range(M)]
# 땅 높이 입력
sum = 0
for i in range(M):
str = input().split(' ')
for j in range(N):
arr[i][j] = int(str[j])
sum = sum + arr[i][j]
# (캘 수 있는 블록 + 가진 블록) / (가로 * 세로) 공식으로 최대 높이를 구한다
maxHeight = (sum + B) // (M * N)
minTime = 2147483647
targetHeight = 0
# 최대 높이까지 반복하며 시간을 비교한다
for i in range(maxHeight + 1):
time = 0
for j in range(M):
for k in range(N):
if arr[j][k] > i:
time = time + (arr[j][k] - i) * 2
elif arr[j][k] < i:
time = time + i - arr[j][k]
# 답이 여러 개 있을 경우 가장 높은 층을 저장하기 위해 >= 조건 사용
if minTime >= time:
minTime = time
targetHeight = i
print(minTime, end=' ')
print(targetHeight)
if __name__ == "__main__":
main()
import Foundation
func main() {
// 가로, 세로, 인벤토리 입력
var str = readLine()!.split(separator: " ")
var M = Int(str[0])!
var N = Int(str[1])!
var B = Int(str[2])!
// 땅 높이 입력
var sum = 0
var map = Array(repeating: Array(repeating: 0, count: N), count: M)
for i in 0..<M {
str = readLine()!.split(separator: " ")
for j in 0..<N {
map[i][j] = Int(str[j])!
sum += map[i][j]
}
}
// (캘 수 있는 블록 + 가진 블록) / (가로 * 세로) 공식으로 최대 높이를 구한다
var maxHeight = (sum + B) / (M * N)
var minTime = Int.max
var targetHeight = 0
// 최대 높이까지 반복하며 시간을 비교한다
for i in 0...maxHeight {
var time = 0
for j in 0..<M {
for k in 0..<N {
if map[j][k] > i {
time += (map[j][k] - i) * 2
} else if map[j][k] < i {
time += i - map[j][k]
}
}
}
// 답이 여러 개 있을 경우 가장 높은 층을 저장하기 위해 >= 조건 사용
if minTime >= time {
minTime = time
targetHeight = i
}
}
print(minTime, targetHeight)
}
main()
