SW Expert Academy - 1949. 등산로 조성

3 minute read

문제링크

문제설명

  • N x N 크기의 등산로를 만들기 위한 부지가 주어지고, 이곳에 최대한 긴 등산로를 만들어야 한다.
  • 각 칸의 숫자는 지형의 높이를 나타낸다.
  • 등산로는 아래와 같은 규칙으로 만들어진다.
    • 등산로는 가장 높은 봉우리에서 시작해야 한다.
    • 등산로는 산으로 올라갈 수 있도록 반드시 높은 지형에서 낮은 지형으로 가로 또는 세로 방향으로 연결이 되어야 한다.
    • 긴 등산로를 만들기 위해 딱 한 곳을 정해서 최대 K 깊이만큼 지형을 깎는 공사를 할 수 있다.
  • 만들 수 있는 가장 긴 등산로를 찾아 길이를 출력하라

문제접근1

  • 브루트포스 + 백트래킹 로 해결한다.
  • 등산로를 만들 시작 지점은 가장 높은 봉우리에서 시작해야 하므로 시작점으로 가능한 봉우리의 좌표 정보를 배열에 담아둔다.
  • 딱 한 곳의 지형을 최대 K 깊이만큼 깎아서 높이를 낮출 수 있고, 1 보다 작은 높이로도 만들 수 있으므로 모든 칸에 대해서 0~K 만큼 지형을 깎아가며 완전탐색(백트래킹)을 한다.
  • 백트래킹 구조
    • 현재 이동한 칸의 높이가 이전 땅의 높이보다 높거나 같은 경우 현재까지의 거리를 반환하고 이전 탐색 지점으로 되돌아 간다.
    • 상하좌우로 이동하며 재귀 호출로 등산로를 탐색한다.

구현1

#include <iostream>
#include <memory.h>
#include <vector>
#include <queue>
#include <algorithm>
#define MAX_N 8

using namespace std;

int board[MAX_N][MAX_N];
int N, K;
int dx[4] = { -1, 1, 0, 0 };
int dy[4] = { 0, 0, -1, 1 };
vector<pair<int, int>> startPoints;
int visited[MAX_N][MAX_N];

int makeTrack(int xpos, int ypos, int height, int dist) {
	// 이동한 곳의 높이가 이동하기 전 높이와 같거나 더 높은 경우
	if (board[xpos][ypos] >= height && dist > 0) {
		return dist;
	}

	int ret = 0;
	for (int dir = 0; dir < 4; dir++) {
		int nx = xpos + dx[dir];
		int ny = ypos + dy[dir];
		if (nx < 0 || nx >= N || ny < 0 || ny >= N) continue;
		if (visited[nx][ny])continue;
		visited[nx][ny] = true;
		ret = max(ret, makeTrack(nx, ny, board[xpos][ypos], dist + 1));
		visited[nx][ny] = false;
	}
	return ret;
}

void updateStartPoints(int maxHeight) {
	for (int r = 0; r < N; r++) {
		for (int c = 0; c < N; c++) {
			if (maxHeight == board[r][c])
				startPoints.push_back(make_pair(r, c));
		}
	}
}

int main(int argc, char** argv)
{
	int test_case;
	int T;
	// freopen("input.txt", "r", stdin);
	cin >> T;
	for (test_case = 1; test_case <= T; ++test_case)
	{
		cin >> N >> K;
		int maxHeight = 0;
		startPoints.clear();
		for (int r = 0; r < N; r++) {
			for (int c = 0; c < N; c++) {
				cin >> board[r][c];
				if (maxHeight < board[r][c]) maxHeight = board[r][c];
			}
		}
		updateStartPoints(maxHeight);
		int ans = 0;
		for (int r = 0; r < N; r++) {
			for (int c = 0; c < N; c++) {
				for (int i = 0; i <= K; i++) {
					board[r][c] -= i;
					for (auto ele : startPoints) {
						memset(visited, false, sizeof(visited));
						ans = max(ans, makeTrack(ele.first, ele.second, board[ele.first][ele.second], 0));
					}
					board[r][c] += i;
				}
			}
		}
		cout << "#" << test_case << " " << ans << "\n";
	}
	return 0;//정상종료시 반드시 0을 리턴해야합니다.
}

문제접근2

  • 공사를 할 수 있는 경우를 확인하며 백트래킹을 한다.

구현2

#include <iostream>
#include <vector>
#include <memory.h>
#include <algorithm>
#include <queue>

#define MAX_N 8
using namespace std;

int N, K;
int board[MAX_N][MAX_N];
int visited[MAX_N][MAX_N];
int highest;
vector<pair<int, int>> startPoints;
int dx[4] = { -1, 1, 0, 0 };
int dy[4] = { 0, 0, -1, 1 };
int ans = 0;

void input()
{
	memset(board, 0, sizeof(board));
	startPoints.clear();
	highest = 0;
	ans = 0;
	cin >> N >> K;
	for (int r = 0; r < N; r++) {
		for (int c = 0; c < N; c++) {
			cin >> board[r][c];
			highest = max(highest, board[r][c]);
		}
	}

	for (int r = 0; r < N; r++) {
		for (int c = 0; c < N; c++) {
			if (board[r][c] == highest) startPoints.push_back(make_pair(r, c));
		}
	}
}

void backtracking(int cx, int cy, bool bcut, int clength)
{
	ans = max(ans, clength);

	for (int dir = 0; dir < 4; dir++) {
		int nx = cx + dx[dir];
		int ny = cy + dy[dir];
		if (nx < 0 || nx >= N || ny < 0 || ny >= N) continue;
		if (visited[nx][ny]) continue;
		visited[nx][ny] = true;
		int nheight = board[nx][ny];
		if (nheight < board[cx][cy]) backtracking(nx, ny, bcut, clength + 1);
		else if (bcut == false && (nheight - board[cx][cy] + 1) <= K) {
			board[nx][ny] = board[cx][cy] - 1;
			backtracking(nx, ny, true, clength + 1);
			board[nx][ny] = nheight;
		}
		visited[nx][ny] = false;
	}
}

void solution()
{
	for (auto ele : startPoints) {
		memset(visited, false, sizeof(visited));
		visited[ele.first][ele.second] = true;
		backtracking(ele.first, ele.second, false, 1);
	}
}

int main(int argc, char** argv)
{
	int test_case;
	int T = 0;
	// freopen("sample_input.txt", "r", stdin);
	cin >> T;
	
	for (test_case = 1; test_case <= T; ++test_case)
	{
		input();
		solution();
		cout << "#" << test_case << " " << ans << endl;
	}
	return 0;//정상종료시 반드시 0을 리턴해야합니다.
}

Leave a comment