본문 바로가기
Algorithm/🏫assignment

# Driving License

by yewoneeee 2022. 11. 14.

# 문제

 

# 입력 및 출력

 

 

# 풀이

어처피 회전 횟수를 제외하면 세로 M-1, 가로 N-1칸씩 이동하는 것은 정해져 있다.

따라서 L * {(M-1) + (N-1)}은 고정이고 여기에 회전 횟수만 최소로 해서 더해주면 결과 값을 구할 수 있다.

 

문제는 연료이다.

주어진 연료 G 이하로 사용하면서 회전 횟수를 최소로 만들어야 한다.

 

처음엔 pair을 사용해서 하려다가 너무 복잡해지기도 하고 접근이 힘들어져서 4차원 배열을 선택했다.

dp[x][y][k][l] => (x, y) 좌표에서 회전수가 k번이고, 방향은 l일 때의 최소 연료 합

 

일단 (x,y) 좌표에서 회전수는 max(x, y) * 2를 절대 넘지 않는다.

예) (2,2) 에서는 최대 3번, (2,4) 에서는 최대 4번, (3,3) 에서는 최대 5번 

따라서 max(x,y) * 2 만큼 반복하면 널널하게 확인할 수 있다.

 

이제 방향을 확인해야 한다.

오른쪽으로 가는 경우를 0으로 두고, 아래로 가는 경우를 1로 둔다.

1) 오른쪽으로 가는 경우 (방향이 0 인 경우)

-> ㄴ, ㅡ 방향이 있다.

이 경우는 dp[i][j][k][0] = min(dp[i][j-1][k-1][1], dp[i][j-1][k][0]) + hor[i][j-1]

2) 아래쪽으로 가는 경우 (방향이 1인 경우)

-> ㄱ, ㅣ 방향이 있다.

이 경우는 dp[i][j][k][1] = min(dp[i-1][j][k-1][0], dp[i-1][j][k][1]) + ver[i-1][j]

 

이렇게 dp 값을 채우고 나서는 (M-1, N-1) 좌표에서 연료 합이 G 이하이면서 최소 회전 수를 가지는 경우를 찾으면 된다.

자세한 부분은 주석을 참고하도록!

#include <fstream>
#include <iostream>
#include <algorithm>
#define INF 987654321
using namespace std;

ifstream fin("drive.inp");
ofstream fout("drive.out");

int dp[101][101][202][2]; // [r][c][rotation time][direction] => (r,c) 위치에서 회전수가 rotation time 일 때 최소 fuel
int hor[101][101]; // fuel of horizontal segment
int ver[101][101]; // fuel of vertical segment

// hor, ver 배열 입력받음
void input(int m, int n) { 
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < n - 1; j++) {
			fin >> hor[i][j];
		}
	}
	for (int i = 0; i < m - 1; i++) {
		for (int j = 0; j < n; j++) {
			fin >> ver[i][j];
		}
	}
}

// dp INF 값으로 초기화 및 1번이라도 회전하면 갈 수 없는 부분 초기화
void init(int m, int n) { 
	int max_turn = max(m, n) * 2; // 회전 수 상한 값
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < n; j++) {
			for (int k = 0; k < max_turn; k++) {
				dp[i][j][k][0] = dp[i][j][k][1] = INF;
			}
		}
	}
	dp[0][0][0][0] = dp[0][0][0][1] = 0; // (0,0)
	for (int i = 1; i < n; i++) { // [0][~]
		dp[0][i][0][0] = dp[0][i - 1][0][0] + hor[0][i - 1];
	}
	for (int i = 1; i < m; i++) { // [~][0]
		dp[i][0][0][1] = dp[i - 1][0][0][1] + ver[i - 1][0];
	}
}

void make_dp(int m, int n) {
	for (int i = 1; i < m; i++) {
		for (int j = 1; j < n; j++) {
			int max_turn = max(i, j) * 2;
			for (int k = 1; k < max_turn; k++) {
				// [i][j][k][0](오른쪽) : ㄴ, ㅡ 방향 (회전하는 경우, 회전하지 않는 경우)
				dp[i][j][k][0] = min(dp[i][j - 1][k - 1][1], dp[i][j - 1][k][0]) + hor[i][j - 1];
				// [i][j][k][1](아래) : ㄱ, ㅣ 방향
				dp[i][j][k][1] = min(dp[i - 1][j][k - 1][0], dp[i - 1][j][k][1]) + ver[i - 1][j];
			}
		}
	}
}

// 주어진 fuel 값을 만족하는 최소 회전 수를 찾음
int find(int m, int n, int fuel, int time) {
	int max_turn = max(n, m) * 2;
	for (int k = 0; k < max_turn; k++) {
		if (dp[m - 1][n - 1][k][0] <= fuel || dp[m - 1][n - 1][k][1] <= fuel) {
			return time * (m - 1 + n - 1) + k;
		}
	}
	return -1;
}

int main() {
	int T, M, N, L, G;
	fin >> T;
	while (T--) {
		fin >> M >> N >> L >> G;
		input(M, N);
		init(M, N);
		make_dp(M, N);
		int result = find(M, N, G, L);
		fout << result << "\n";
	}
}

 

처음엔 3차원 배열로 풀었다가 4차원으로 합칠 수 있을 것 같아서 합쳤다

 

'Algorithm > 🏫assignment' 카테고리의 다른 글

# MST  (0) 2022.11.09
# Adding Ways  (0) 2022.10.31
# Card Game  (0) 2022.10.29
# 카드 선택  (0) 2022.10.08
# 정육면체  (0) 2022.10.07

댓글