[백준] 치킨 배달

최동혁·2022년 12월 6일
0

백준

목록 보기
48/68

치킨 배달

solved_ac[Class4][치킨 배달](https://www.acmicpc.net/problem/15686)

문제

크기가 N×N인 도시가 있다. 도시는 1×1크기의 칸으로 나누어져 있다. 도시의 각 칸은 빈 칸, 치킨집, 집 중 하나이다. 도시의 칸은 (r, c)와 같은 형태로 나타내고, r행 c열 또는 위에서부터 r번째 칸, 왼쪽에서부터 c번째 칸을 의미한다. r과 c는 1부터 시작한다.

이 도시에 사는 사람들은 치킨을 매우 좋아한다. 따라서, 사람들은 "치킨 거리"라는 말을 주로 사용한다. 치킨 거리는 집과 가장 가까운 치킨집 사이의 거리이다. 즉, 치킨 거리는 집을 기준으로 정해지며, 각각의 집은 치킨 거리를 가지고 있다. 도시의 치킨 거리는 모든 집의 치킨 거리의 합이다.

임의의 두 칸 (r1, c1)과 (r2, c2) 사이의 거리는 절대값(r1-r2) + 절대값(c1-c2)로 구한다.

예를 들어, 아래와 같은 지도를 갖는 도시를 살펴보자.

0 2 0 1 0
1 0 1 0 0
0 0 0 0 0
0 0 0 1 1
0 0 0 1 2

0은 빈 칸, 1은 집, 2는 치킨집이다.

(2, 1)에 있는 집과 (1, 2)에 있는 치킨집과의 거리는 절대값(2-1) + 절대값(1-2) = 2, (5, 5)에 있는 치킨집과의 거리는 절대값(2-5) + 절대값(1-5) = 7이다. 따라서, (2, 1)에 있는 집의 치킨 거리는 2이다.

(5, 4)에 있는 집과 (1, 2)에 있는 치킨집과의 거리는 절대값(5-1) + 절대값(4-2) = 6, (5, 5)에 있는 치킨집과의 거리는 절대값(5-5) + 절대값(4-5) = 1이다. 따라서, (5, 4)에 있는 집의 치킨 거리는 1이다.

이 도시에 있는 치킨집은 모두 같은 프랜차이즈이다. 프렌차이즈 본사에서는 수익을 증가시키기 위해 일부 치킨집을 폐업시키려고 한다. 오랜 연구 끝에 이 도시에서 가장 수익을 많이 낼 수 있는 치킨집의 개수는 최대 M개라는 사실을 알아내었다.

도시에 있는 치킨집 중에서 최대 M개를 고르고, 나머지 치킨집은 모두 폐업시켜야 한다. 어떻게 고르면, 도시의 치킨 거리가 가장 작게 될지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N(2 ≤ N ≤ 50)과 M(1 ≤ M ≤ 13)이 주어진다.

둘째 줄부터 N개의 줄에는 도시의 정보가 주어진다.

도시의 정보는 0, 1, 2로 이루어져 있고, 0은 빈 칸, 1은 집, 2는 치킨집을 의미한다. 집의 개수는 2N개를 넘지 않으며, 적어도 1개는 존재한다. 치킨집의 개수는 M보다 크거나 같고, 13보다 작거나 같다.

출력

첫째 줄에 폐업시키지 않을 치킨집을 최대 M개를 골랐을 때, 도시의 치킨 거리의 최솟값을 출력한다.

예제 입력 1

5 3
0 0 1 0 0
0 0 2 0 1
0 1 2 0 0
0 0 1 0 0
0 0 0 0 2

예제 출력 1

5

예제 입력 2

5 2
0 2 0 1 0
1 0 1 0 0
0 0 0 0 0
2 0 0 1 1
2 2 0 1 2

예제 출력 2

10

예제 입력 3

5 1
1 2 0 0 0
1 2 0 0 0
1 2 0 0 0
1 2 0 0 0
1 2 0 0 0

예제 출력 3

11

예제 입력 4

5 1
1 2 0 2 1
1 2 0 2 1
1 2 0 2 1
1 2 0 2 1
1 2 0 2 1

예제 출력 4

32

문제 해석

2의 갯수가 치킨집의 갯수인데, 폐업시키지 말아야할 치킨 집을 M으로 입력을 받는다. 즉, 총 치킨 집 중에 M개의 치킨집을 조합을 이용하여 뽑아낸 다음 최소 비용을 가지고 있는 조합을 끄집어 내서 정답을 출력해 내야 하는데, bfs, dfs, 등 여러가지 알고리즘을 생각해봤지만 효율도 안나올거 같아서 완전 탐색을 이용해서 풀기로 생각을 하였다.

풀이

  • 집인 곳의 좌표를 저장해 줄 graph_home 선언
  • 치킨집인 곳의 좌표를 저장해 줄 graph_chicken 선언
  • 왜 [0][0] 배열을 0으로 초기화를 해줬냐면 우리는 0번 치킨집이 아닌 1번 치킨집 부터 시작할 것이기 때문이다.
집 1(0,3) 
	치킨집 1(0,1) 거리 2
	치킨집 2(3,0) 거리 6
	치킨집 3(4,0) 거리 7
	치킨집 4(4,1) 거리 6
	치킨집 5(4,4) 거리 5

집 2(1,0) 
	치킨집 1(0,1) 거리 2
	치킨집 2(3,0) 거리 2
	치킨집 3(4,0) 거리 3
	치킨집 4(4,1) 거리 4
	치킨집 5(4,4) 거리 7

집 3(1,2) 
	치킨집 1(0,1) 거리 2
	치킨집 2(3,0) 거리 4
	치킨집 3(4,0) 거리 5
	치킨집 4(4,1) 거리 4
	치킨집 5(4,4) 거리 5

집 4(3,3) 
	치킨집 1(0,1) 거리 5
	치킨집 2(3,0) 거리 3
	치킨집 3(4,0) 거리 4
	치킨집 4(4,1) 거리 3
	치킨집 5(4,4) 거리 2

집 5(3,4) 
	치킨집 1(0,1) 거리 6
	치킨집 2(3,0) 거리 4
	치킨집 3(4,0) 거리 5
	치킨집 4(4,1) 거리 4
	치킨집 5(4,4) 거리 1

집 6(4,3) 
	치킨집 1(0,1) 거리 6
	치킨집 2(3,0) 거리 4
	치킨집 3(4,0) 거리 3
	치킨집 4(4,1) 거리 2
	치킨집 5(4,4) 거리 1
  • 위에서 보듯이 각 집을 기준으로 잡아서 각 집들이 각 치킨집에 가는데 걸리는 거리를 2차원 배열로 선언해준다.
  • 예를들면 집 1이 치킨 집 4에 가는데 걸리는 거리는 distance_chicken[1][4]이다.
  • combinations 함수를 이용하여 총 치킨집 중에 M개를 고를 경우의 수 들을 comb_list에 넣어준다.
  • len(graph_home)번을 돈다. 이건 총 집의 갯수인데 위에서 구한 경우의 수만큼 고른 치킨 집과 집들의 거리를 다 더해서 min값을 추출해낸다.
  • 예를 들면 5개의 치킨집 중 2개를 폐업시키지 말고 나둬야 한다면 조합이 (1,2), (1,3) 등등 이런식으로 나올 것이다. 1,3이 나왔을 때 각 집들이 치킨집 1과 치킨집 3 중에 가까운 거리에 있는 거리를 min_sum에 더한 후 넣어준다.
  • 그렇게 되면 모든 경우의 수에 있는 것들 중 각자 집에서 치킨집에 가까운 경우를 다 더한 것들이 min_sum 리스트에 들어가게 된다.
  • min_sum에서 sort를 써서 오름차순으로 정렬을 해주면 0번지에 있는 값이 모든 경우 중에 가장 작은 값이 된다.
import sys
import itertools

N, M = map(int, sys.stdin.readline().split())

graph = []

for i in range(N):
    graph.append(list(map(int, sys.stdin.readline().split())))

graph_home = [[0]]
graph_chicken = [[0]]

for i in range(N):
    for j in range(N):
        if graph[i][j] == 1:
            graph_home.append((i, j))
        elif graph[i][j] == 2:
            graph_chicken.append((i, j))
            
           
distance_chicken = [[0] * len(graph_home) for _ in range(len(graph_chicken))] 

for i in range(1, len(graph_chicken)):
    for j in range(1, len(graph_home)):
        distance_chicken[i][j] = abs(graph_chicken[i][0] - graph_home[j][0]) + abs(graph_chicken[i][1] - graph_home[j][1])

min_compare = 10 ** 9      

comb_list = list(itertools.combinations(range(1, len(graph_chicken)), M))
min_sum = [0] * len(comb_list)
for k in range(1, len(graph_home)):
    for i in range(len(comb_list)):
        for j in comb_list[i]:
            if min_compare > distance_chicken[j][k]:
                min_compare = distance_chicken[j][k]
        min_sum[i] += min_compare
        min_compare = 10 ** 9
        
min_sum.sort()
print(min_sum[0])

고찰

전에 썻던 조합 함수인 combinations 함수와 2차원 배열들을 적절히 잘 이용을 한 것 같다. 생각을 많이 하긴 했지만 초반에 치킨집을 기준으로 하지 않고 집을 기준으로 해서 생각한게 많이 도움이 된 것 같다. 전에부터 안좋은 버릇이 머릿속으로 혼자 생각하고 코딩을 하면서 계속 고쳐나갔는데, 그것보다 초반에 설계를 해놓고 어떤식으로 코딩을 할지 생각을 하고 시작하는 것이 훨씬 더 효율적이다 라는 것을 깨달았다.

profile
항상 성장하는 개발자 최동혁입니다.

0개의 댓글