문제 15686번 (백트래킹, 브루트포스)
: 크기가 N×N인 도시
도시의 각 칸은 빈 칸, 치킨집, 집 중 하나
: 사람들은 "치킨 거리"라는 말을 주로 사용
치킨 거리는 집과 가장 가까운 치킨집 사이의 거리
각각의 집은 치킨 거리를 가지고 있고 도시의 치킨 거리는 모든 집의 치킨 거리의 합이다.
거리는 |r1-r2| + |c1-c2|로 구한다
: 0은 빈 칸, 1은 집, 2는 치킨집
이 도시에 있는 치킨집은 모두 같은 프랜차이즈이며 일부 치킨집을 폐업시키려고 한다
이 도시에서 가장 수익을 많이 낼 수 있는 치킨집의 개수는 최대 M개
나머지 치킨집은 모두 폐업시켜야 한다
어떻게 고르면, 도시의 치킨 거리가 가장 작게 될지 구하는 프로그램을 작성
[입력]
: 첫째 줄에 N(2 ≤ N ≤ 50)과 M(1 ≤ M ≤ 13)
: 둘째 줄부터 N개의 줄에는 도시의 정보
0은 빈 칸, 1은 집, 2는 치킨집 (1 ≤ 집의 개수 < 2N, M ≤ 치킨집의 개수 ≤ 13)
[출력]
: 첫째 줄에 폐업시키지 않을 치킨집을 최대 M개를 골랐을 때, 도시의 치킨 거리의 최솟값을 출력
[과정]
탐색하자 → 브루트포스 / dfs → 조건이 있다 → 백트래킹
조건에 부합하지 않아서 백트래킹을 쓴다기 보다 여러조합을 만들어놓기 위함이다
종료조건에서 부합하지 않는지 모든 조합들을 탐색한다
종료 조건) M개의 거리를 뽑을 경우 종료
count가 M개가 되면 치킨거리 합을 계산한다
- home좌표를 하나 고정하고 chicken좌표를 순차적으로 돌리면서 치킨 거리 계산
- 완전탐색을 통해 모든 거리를 계산하며 min값만 뽑아낸다
- 집을 기준으로 각 지점마다의 최소 치킨 거리를 M만큼 더한다
- 이중에서 가장 작은 최소값을 구하고 출력한다
1. 치킨 지점과 집 지점만 따로 뽑기
- 원활한 계산을 위해 ArrayList를 사용한다
2. 백트래킹의 별도의 조건은 없다 다만 min을 통해서 최소값을 추려나간다
[코드]
import java.io.*;
import java.util.*;
public class Main{
static int N,M,arr[][];
static boolean visit[];
static int result = Integer.MAX_VALUE;
static List<int[]> home = new ArrayList<>();
static List<int[]> chicken = new ArrayList<>();
public static void main(String[] args)throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
arr = new int[N][N];
for(int i=0;i<N;i++){
st = new StringTokenizer(br.readLine());
for(int j=0;j<N;j++){
arr[i][j] = Integer.parseInt(st.nextToken());
if(arr[i][j]==1){
home.add(new int[]{i,j});
}
else if(arr[i][j]==2){
chicken.add(new int[]{i,j});
}
}
}
visit = new boolean[chicken.size()];
back(0,0);
System.out.println(result);
}
static void back(int start, int count){
if(count==M){
int sum = 0;
for(int[]h: home){
int min = Integer.MAX_VALUE;
for(int c =0;c<chicken.size();c++){
if(visit[c]){
int dist = Math.abs(h[0]-chicken.get(c)[0])+Math.abs(h[1]-chicken.get(c)[1]);
min = Math.min(min,dist);
}
}
sum+=min;
}
result = Math.min(result,sum);
return;
}
for(int i=start;i<chicken.size();i++){
visit[i]=true;
back(i+1, count+1);
visit[i]=false;
}
}
}
[해설]
: BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken()); 입력1
M = Integer.parseInt(st.nextToken()); 입력2
arr = new int[N][N]; 지도
: for(int i=0;i<N;i++){
st = new StringTokenizer(br.readLine());
for(int j=0;j<N;j++){ 지도 입력받기
arr[i][j] = Integer.parseInt(st.nextToken());
if(arr[i][j]==1){ 집의 좌표만 따로 뽑기
home.add(new int[]{i,j});
}
else if(arr[i][j]==2){ 치킨 좌표만 따로 뽑기
chicken.add(new int[]{i,j});
}
}
}
: visit = new boolean[chicken.size()]; chicken 지점수대로 체크
back(0,0); 백트래킹 시작
System.out.println(result); 최종 결과 출력
: static void back(int start, int count){ 시작할 치킨인덱스(도착지점) / 연산횟수
if(count==M){ 연산횟수가 M일 경우 종료
int sum = 0; 연산 합 구하기
for(int[]h: home){ 시작지점인 집의 좌표를 순차적으로 고정
int min = Integer.MAX_VALUE; 그때마다 min값 초기화
for(int c =0;c<chicken.size();c++){ 치킨의 좌표 즉 도착지점 순차적으로 변경
if(visit[c]){ 방문한 곳들만 치킨 거리 계산
int dist = Math.abs(h[0]-chicken.get(c)[0])+Math.abs(h[1]-chicken.get(c)[1]);
min = Math.min(min,dist); 집을 기준으로 해당 집만의 최소 치킨 거리 저장
}
}
sum+=min; 치킨거리 모두 저장
}
result = Math.min(result,sum); 모든 도시의 M개의 치킨거리 중 최소값 출력
return;
}
for(int i=start;i<chicken.size();i++){
visit[i]=true; 방문처리
back(i+1, count+1); 백트래킹 시작
visit[i]=false; 방문 취소처리
}
}
이제 풀어보러 갈께요 :)
* 독학으로 익히는 코딩이라 틀린 것이 있을 수 있습니다. 오류가 있다면 댓글을 통해 알려주세요. 감사합니다. *
'Baekjoon > [6] 백트래킹' 카테고리의 다른 글
[백준] 9663번 N-Queen JAVA (자바) 풀이 (0) | 2024.08.06 |
---|---|
[백준] 1987번 알파벳 JAVA (자바) 풀이 (0) | 2024.08.06 |
[백준] 2529번 부등호 JAVA (자바) 풀이 (0) | 2024.06.25 |
[백준] 10971번 외판원 순회2 JAVA (자바) 풀이 (1) | 2024.06.22 |
[백준] 10974번 모든 순열 JAVA (자바) 풀이 (0) | 2024.05.27 |