-
728x90
문제를 처음 풀었을 때는 시간 초과를 받고, 풀이를 약간 변형해서 통과했다.
첫 번째 풀이 DFS - 시간초과
public static void dfs(int cnt) {// 치킨집을 m개 오픈한 경우 도시의 치킨거리 계산if(cnt == m) {int cityDist = calculate();answer = Math.min(answer, cityDist);}// m개 미만 오픈한 경우 오픈for(int i = 0; i < n; i++) {for(int j = 0; j < n; j++) {if(board[i][j] == 2 && !isOpened[i][j]) {// 오픈해서 dfs 호출isOpened[i][j] = true;chickens.add(new Info(i, j, 2));dfs(cnt + 1);// 호출 후 원상복구isOpened[i][j] = false;chickens.remove(chickens.size() - 1);}}}} // end of dfs첫 번째 풀이에서는 전체 지도(board)의 0행 0열부터 차례대로 찾으며 열려있지 않은 치킨집을 오픈시킨다. 백트래킹 방식으로 동작하기 때문에 같은 조합의 치킨집을 오픈하더라도, 아래와 같은 케이스들이 모두 별개로 취급되어 치킨거리를 계산하게 된다.
- (1, 2), (3, 4), (4, 5)
- (1, 2), (4, 5), (3, 4)
- (4, 5), (1, 2), (3, 4)
- (3, 4), (1, 2), (4, 5)
- (4, 5), (3, 4), (1, 2)
- (3, 4), (4, 5), (1, 2)
만약 (1, 2), (3, 4), (4, 5) 3개의 위치에 치킨집을 오픈한다고 했을 때, 위 6가지 케이스들은 모두 같은 치킨거리를 갖게 되지만 모두 따로 계산되어 각각 치킨거리를 탐색하게 된다. 치킨집의 최대 갯수가 13개인데 위와 같이 자리를 모두 바꾼다면? 13! = 약 60억으로 시간초과가 날 수 밖에 없다.
이런 문제를 두 번째 문제에서 스택을 활용해 해결한다.
두 번째 풀이 DFS - 성공
public static void dfs(int start, int cnt) {// 치킨집을 m개 오픈한 경우 도시의 치킨거리 계산if(cnt == m) {int cityDist = calculate();answer = Math.min(answer, cityDist);}// m개 미만 오픈한 경우 오픈for(int i = start; i < chickens.size(); i++) {Info now = chickens.get(i);// 오픈해서 dfs 호출selected.push(now);dfs(i + 1, cnt + 1);// 호출 후 원상복구selected.pop();}} // end of dfs이제는 치킨집의 위치를 지도에서 꺼내지 않고, chickens라는 List에서 꺼내온다. 리스트에 미리 치킨집이 들어올 수 있는 위치들을 저장해놓고, 리스트의 인덱스를 dfs 메서드의 인자로 주고받으면서 어디부터 오픈할지를 저장해둔다.
그리고, selected를 Stack으로 두면서 전체 경우의 수를 한 번씩만 탐색하도록 한다. 지도를 처음부터 선택하도록 하는 것이 아닌 인자를 하나 추가하고 다른 자료구조를 사용하는 방식으로 시간복잡도를 매우 개선할 뿐만 아니라 코드 라인 수도 줄이고 풀이도 훨씬 간결해졌다.
두 번째 풀이 전체 코드
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.List;import java.util.Stack;import java.util.StringTokenizer;public class Main {static int n, m, answer;static int[][] board;static List<Info> houses = new ArrayList<>();static List<Info> chickens = new ArrayList<>();static Stack<Info> selected = new Stack<>();public static void main(String[] args) throws Exception{BufferedReader br = new BufferedReader(new InputStreamReader(System.in));StringTokenizer st = new StringTokenizer(br.readLine());n = Integer.parseInt(st.nextToken());m = Integer.parseInt(st.nextToken());board = new int[n][n];answer = Integer.MAX_VALUE;for(int i = 0; i < n; i++) {st = new StringTokenizer(br.readLine());for(int j = 0; j < n; j++) {board[i][j] = Integer.parseInt(st.nextToken());// 집이면 리스트에 추가해놓음if(board[i][j] == 1) {houses.add(new Info(i, j, 1));}else if(board[i][j] == 2) {chickens.add(new Info(i, j, 2));}}}dfs(0, 0);System.out.println(answer);}public static void dfs(int start, int cnt) {// 치킨집을 m개 오픈한 경우 도시의 치킨거리 계산if(cnt == m) {int cityDist = calculate();answer = Math.min(answer, cityDist);}// m개 미만 오픈한 경우 오픈for(int i = start; i < chickens.size(); i++) {Info now = chickens.get(i);// 오픈해서 dfs 호출selected.push(now);dfs(i + 1, cnt + 1);// 호출 후 원상복구selected.pop();}} // end of dfspublic static int calculate() {int cityMinDist = 0;// 집을 하나씩 골라for(int i = 0; i < houses.size(); i++) {Info nowHouse = houses.get(i);int minDist = Integer.MAX_VALUE;// 치킨집마다 거리를 계산하고 최소 치킨거리 갱신 가능하면 갱신for(int j = 0; j < selected.size(); j++) {Info nowChicken = selected.get(j);// 현재 집과 치킨집 거리 계산int dist = Math.abs(nowHouse.row - nowChicken.row)+ Math.abs(nowHouse.col - nowChicken.col);// 최소 치킨거리 갱신 가능하면 갱신minDist = Math.min(dist, minDist);}// 현재 집에서 가장 가까운 치킨거리를 도시 치킨거리에 반영cityMinDist += minDist;}return cityMinDist;}}class Info{int row, col;int type;public Info(int row, int col, int type) {this.row = row;this.col = col;this.type = type;}@Overridepublic String toString() {return "Info [row=" + row + ", col=" + col + ", type=" + type + "]";}}'Algorithms' 카테고리의 다른 글
[BOJ] 10869번 Golang 풀이 (0) 2021.12.13 [BOJ] 5373번 큐빙 Java 풀이 (0) 2021.06.26 [BOJ] 15684번 사다리 조작 Java 풀이 (0) 2021.06.26 [BOJ] 14503번 로봇청소기 Java 풀이 (0) 2021.06.19 [BOJ] 14502번 연구소 Java 풀이 (0) 2021.06.19 댓글
- (1, 2), (3, 4), (4, 5)