본문 바로가기

Algorithm/BeakJoon

[BeakJoon 2618] 경찰차

안녕하세요. 이번 포스팅에서는 백준 2618번 경찰차 문제를 풀어보도록 하겠습니다.

 

해당 문제는

https://www.acmicpc.net/problem/2618

 

2618번: 경찰차

첫째 줄에는 동서방향 도로의 개수를 나타내는 정수 N(5 ≤ N ≤ 1,000)이 주어진다. 둘째 줄에는 처리해야 하는 사건의 개수를 나타내는 정수 W(1 ≤ W ≤ 1,000)가 주어진다. 셋째 줄부터 (W+2)번째 줄

www.acmicpc.net

 

위 링크에서 확인하실 수 있습니다.

 

우선 이 문제는 굉장히 어려운 문제입니다. 그 이유는 문제의 제한입니다.

 

보통 이런 문제는 

int go(int orderIdx, int firstCar, int secondCar) {
	if(orderIdx == w) return 0;
	
	int& ret = dp[orderIdx][firstCar][secondCar]
	if(ret) return ret;
	ret = 1e9;
	
	int ret1 = go(orderIdx+1, orderIdx, secondCar) + getDist(firstCar, orderIdx);
	int ret2 = go(orderIdx+1, firstCar, orderIdx]) + getDist(secondCar, orderIdx);
	
	if(ret1 < ret2) {
		_select[orderIdx] = 1; 
	} else _select[orderIdx] = 2;
	
	return ret = min(ret1, ret2);
}

 

이런 풀이를 떠올리실 수 있습니다. 쉽게 설명하자면, 

 

현재 orderIdx에서 첫 번째 차로 얻는 최솟값 vs 두 번째 차로 얻는 최솟값을 비교하여 최솟값을 저장하는 형태입니다. 이 풀이로 하시면 dp배열이 문제의 인풋에 따라서

 

1000*1000*1000의 dp배열이 필요합니다. 당연히 선언할 수 없습니다.

 

따라서 orderIdx를 사용하지 않고 orderIdx를 얻어내야 합니다. 

 

이전 함수에서 어차피 firstCar와 secondCar 둘중 하나는 이전 orderIdx라는 점을 유의 깊게 보시면 다음 재귀 함수에서 orderIdx를 max(firstCar, secondCar) + 1을 통해 얻을 수 있다는 것을 알 수 있습니다.

 

어차피 인풋으로 orders값이 다 저장된 배열을 가지고 있기 때문에, firstCar, secondCar 모두 인덱스의 형태로 갖고있으면, 더 나중의 인덱스가 선택될 것이기 때문입니다.

 

따라서 위의 코드는 다음과 같이 인풋 배열을 줄이고도 정상적으로 결과를 도출할 수 있습니다.

int go(int firstCar, int secondCar) {
	
	int orderIdx = max(firstCar, secondCar)+1;
	if(orderIdx == w+1) return 0;
	
	int& ret = dp[firstCar][secondCar];
	if(~ret) return ret;
	
	ret=1e9;
	int ret1 = go(orderIdx, secondCar) + getDist(firstCar, orderIdx);	 	
	int ret2 = go(firstCar, orderIdx) + getDist(orderIdx, secondCar);
	
	if(ret1 < ret2) {
		ret=ret1; _select[firstCar][secondCar]=1;
	} else {
		ret=ret2; _select[firstCar][secondCar]=2;
	}
	return ret;
}

 

이제 경로를 어떻게 출력하는지 살펴 보겠습니다.

 

경로는 _select[firstCar][secondCar]에 결과로 선택하는 경찰차의 번호를 넣어 주었습니다. 그리고 출력할 때 알고리즘과 마찬가지로 x y값을 각각 max를 통해 변경해주며 출력해주었습니다.

 

아래는 전체 소스코드입니다.

 

<소스 코드>

#include<bits/stdc++.h>
using namespace std; 
int n, w, res = 1e9, _select[1004][1004], dp[1004][1004];
pair<int,int> orders[1004];
int getDist(int here, int next) {
	pair<int,int> car = orders[here], goal = orders[next];
	if(here==0) car={1,1}; 
	if(next==0) goal={n,n};
	return (abs(car.first - goal.first) + abs(car.second - goal.second));
}
int go(int firstCar, int secondCar) {
	
	int orderIdx = max(firstCar, secondCar)+1;
	if(orderIdx == w+1) return 0;
	
	int& ret = dp[firstCar][secondCar];
	if(~ret) return ret;
	
	ret=1e9;
	int ret1 = go(orderIdx, secondCar) + getDist(firstCar, orderIdx);	 	
	int ret2 = go(firstCar, orderIdx) + getDist(orderIdx, secondCar);
	
	if(ret1 < ret2) {
		ret=ret1; _select[firstCar][secondCar]=1;
	} else {
		ret=ret2; _select[firstCar][secondCar]=2;
	}
	return ret;
}
int main(){
	ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
	cin >> n >> w;
	for(int i = 1; i <= w; i++) {
		int a, b; cin >> a >> b;
		orders[i] = {a,b};
	}
	
	fill(&dp[0][0], &dp[0][0]+1004*1004, -1);
	
	res = go(0, 0);
	cout << res << "\n";
	
	int x=0, y=0;
	while(1) {
		cout << _select[x][y] << "\n";
		if(_select[x][y]==2) y=max(x,y)+1;
		else x=max(x,y)+1;
		
		if(max(x,y)==w) break;	
	}
	return 0; 
}

 

*저의 글에 대한 피드백이나 지적은 언제나 환영합니다.

'Algorithm > BeakJoon' 카테고리의 다른 글

[BeakJoon 2042] 구간 합 구하기  (0) 2021.08.22
[BeakJoon 17623] 괄호  (0) 2021.08.22
[BeakJoon 16235] 나무 재테크  (0) 2021.08.13
[BeakJoon 17070] 파이프 옮기기1  (0) 2021.08.10
[BeakJoon 1700] 멀티탭 스케줄링  (0) 2021.08.09