BOJ : www.acmicpc.net/problem/1486

 

1486번: 등산

첫째 줄에 산의 세로크기 N과 가로크기 M 그리고, T와 D가 주어진다. N과 M은 25보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에 지도가 주어진다. T는 52보다 작거나 같은 자연수이고, D는 1,000

www.acmicpc.net

문제

세준이가 가려고하는 산의 지도가 입력으로 주어진다. 산의 지도를 M이라고 했을 때, M[i][j]는 (i,j)의 높이가 M[i][j]라는 것을 의미한다. 그 값이 'A'-'Z'일 때는 0-25를 뜻하는 것이고, 'a'-'z'일 때는, 26-51을 뜻한다.

세준이의 호텔은 (0,0)에 있다. 그리고, 세준이는 지금 위치에서 바로 인접한 정수 좌표 중 높이의 차이가 T보다 크지 않은 곳으로만 다닐 수 있다.

만약 세준이가 현재 위치에서 높이가 낮은 곳이나 같은 곳으로 이동한다면 시간은 1초가 걸린다. 하지만 높은 곳으로 이동한다면 두 위치의 높이의 차이의 제곱만큼 시간이 걸린다. 예를 들어 높이가 5에서 높이가 9인 곳으로 간다면, 시간은 (5-9)2=16초가 걸린다. 하지만, 높이가 9인 곳에서 5인 곳으로 간다면 시간은 1초가 걸린다.

산의 지도와, T, 그리고 어두워지는 시간 D가 주어졌을 때, 세준이가 D보다 크지 않은 시간 동안 올라갈 수 있는 최대 높이를 구하는 프로그램을 작성하시오.(세준이는 호텔에서 출발해서 호텔로 돌아와야 한다.)

 

입력

첫째 줄에 산의 세로크기 N과 가로크기 M 그리고, T와 D가 주어진다. N과 M은 25보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에 지도가 주어진다. T는 52보다 작거나 같은 자연수이고, D는 1,000,000보다 작거나 같은 자연수이다.

 

출력

첫째 줄에 세준이가 갈 수 있는 가장 높은 곳의 높이를 출력한다.

 

풀이

문제에 접근해보면 (0.0)에서 출발해서 어느 지점 (y,x) 을 지나 다시 제자리(0.0) 으로 돌아오는 문제다

그 과정에서 y,x -> yy,xx 로 이동할 때

y,x 가 yy,xx 보다 같거나 크면 시간이 +1

y,x 가 yy,xx 보다 작으면 그 차이만큼 제곱한 시간을 더해주게 된다

결국 (0,0)에서 (y,x)로 이동하는데 걸리는 시간D1 과 (y,x)에서 (0,0)으로 이동하는데 걸리는 시간 D2

D1 + D2가 D보다 작은 모든 경우 중에서 가장 큰 좌표의 높이를 구하는 문제이다

 

결론 => 

(0,0)으로부터 모든 좌표로의 최소시간

(y,x)으로부터 모든 좌표로의 최소시간

다익스트라를 사용하여 문제를 해결

 

minHeap을 사용해야하는데 계속 maxHeap을 사용하는 실수를 하네..?

 

더보기
#include <iostream>
#include <algorithm>
#include <queue>
#include <string>
#include <cmath>
using namespace std;

struct pii{
	int time, y, x;
};

struct compare {
	bool operator()(pii& a, pii& b) {
		return a.time > b.time;
	}
};

#define INF 987654321
int my[4] = { -1,1,0,0 };
int mx[4] = { 0,0,1,-1 };
string arra[30];
int arr[30][30];
int dp[30][30][30][30];
int N, M, T, D;

void getDijkstra(int sy, int sx) {

	priority_queue<pii, vector<pii>, compare> pq;
	pq.push({ 0, sy, sx });
	dp[sy][sx][sy][sx] = 0;
	while (!pq.empty()) {
		int y = pq.top().y;
		int x = pq.top().x;
		int time = pq.top().time;
		pq.pop();

		if (dp[sy][sx][y][x] < time) continue;

		for (int i = 0; i < 4; i++) {
			int yy = y + my[i];
			int xx = x + mx[i];
			if (yy < 0 || xx < 0 || yy >= N || xx >= M) continue;

			if (abs(arr[y][x] - arr[yy][xx]) > T) continue;
			
			int nextTime;
			if (arr[y][x] >= arr[yy][xx]) {
				nextTime = time + 1;
			}
			else {
				nextTime = pow(abs(arr[y][x] - arr[yy][xx]), 2) + time;
			}

			if (nextTime < dp[sy][sx][yy][xx]) {
				dp[sy][sx][yy][xx] = nextTime;
				pq.push({ nextTime, yy, xx });
			}
		}
	}
}

int dijkstra() {

	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			for (int a = 0; a < N; a++) {
				for (int b = 0; b < M; b++) {
					dp[i][j][a][b] = INF;
				}
			}
		}
	}

	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			getDijkstra(i, j);
		}
	}

	int answer = 0;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			if (dp[0][0][i][j] == INF || dp[i][j][0][0] == INF) continue;
			int calTime = dp[0][0][i][j] + dp[i][j][0][0];
			if (calTime <= D) {
				answer = max(answer, arr[i][j]);
			}
		}
	}

	return answer;
}

int main() {
	ios::sync_with_stdio(0), cin.tie(0);

	cin >> N >> M >> T >> D;
	for (int i = 0; i < N; i++) {
		cin >> arra[i];
		for (int j = 0; j < M; j++) {
			if (arra[i][j] >= 'A' && arra[i][j] <= 'Z') arr[i][j] = arra[i][j] - 'A';
			else arr[i][j] = arra[i][j] - 'a' + 26;
		}
	}

	cout << dijkstra();
}

 

'algorithm > BOJ' 카테고리의 다른 글

[JS & Stack] 10828번: 스택  (0) 2023.07.19
BOJ 2273번 줄 서기  (0) 2021.04.17
BOJ 3584번 가장 가까운 공통 조상  (0) 2021.03.31
BOJ 1103번 게임  (0) 2021.03.16
BOJ 2194번 유닛 이동시키기 C++  (0) 2020.07.05

BOJ : www.acmicpc.net/problem/2273

 

2273번: 줄 서기

첫째 줄에 N(1≤N≤256), M(1≤M≤100,000)이 주어진다. M은 키를 비교한 회수이다. 다음 M개의 줄에는 키를 비교한 두 학생의 번호 A, B가 주어진다. 이는 학생 A가 학생 B의 앞에 서야 한다는 의미이다.

www.acmicpc.net

 

문제

N명의 학생들이 키 순서대로 줄을 서려고 한다. 각 학생의 키를 직접 재서 정렬하면 간단하겠지만, 마땅한 방법이 없어서 두 학생의 키를 비교하는 방법을 사용하기로 하였다. 그나마도 모든 학생들을 다 비교해 본 것이 아니고, 일부 학생들의 키만을 비교해 보았다.

일부 학생들의 키를 비교한 결과가 주어졌을 때, 각 학생이 설 수 있는 위치의 범위를 구하는 프로그램을 작성하시오.

 

입력

첫째 줄에 N(1≤N≤256), M(1≤M≤100,000)이 주어진다. M은 키를 비교한 회수이다. 다음 M개의 줄에는 키를 비교한 두 학생의 번호 A, B가 주어진다. 이는 학생 A가 학생 B의 앞에 서야 한다는 의미이다. 같은 학생들을 여러 번 비교했을 수도 있다.

 

출력

N개의 줄에 각 학생이 설 수 있는 위치의 범위를 출력한다. 불가능한 경우에는 첫째 줄에 -1을 출력한다.

 

 

풀이

요즘 문제 풀기가 바빠 포스팅을 잘 안했는데, 이번 문제는 내가 코드를 꼼꼼히 작성하지 않는다는 것과 아무 생각없이 문제를 접근한다는거에 대한 반성의 의미로 포스팅하게 되었다.

이미 비교되어 있는 정점에서 서로 다른 정점과의 비교값을 얻기 위한 이와 같은 문제는 플로이드 와샬로 접근해야한다.

 

처음에는 플로이드와샬로 접근해놓고 모든 조합을 구하기 위해 DFS를 사용했는데,, 왜그런거야..

이 문제의 해결법은 나보다 키가 작은사람, 키가 큰사람의 인원수만 구하면 되는건데........

현재 학생과 다른 학생들 간의 대소관계를 비교하는 플로이드 과정이 끝나면

현재 학생이 가질 수 있는 범위는 다음과 같아진다

left : 1, right : N 일때

나보다 큰 학생이 있는 경우 left+1

나보다 작은 학생이 있는 경우 right-1

이렇게 간단하게 범위를 계산할 수 있게 된다

 

더보기
#include <iostream>
using namespace std;
int arr[257][257], N, M, s, e, l, r;
int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin >> N >> M;
	
	for (int i = 0; i < M; i++) {
		cin >> s >> e;
		if (arr[s][e] == -1 || arr[e][s] == 1) {
			cout << "-1";
			return 0;
		}
		arr[s][e] = 1;
		arr[e][s] = -1;
	}
	for (int k = 1; k <= N; k++) {
		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++) {
				if (i == j) continue;
				if (arr[i][j] != 0) {
					if ((arr[i][j] == arr[j][i]) || (arr[i][k] == 1 && arr[k][j] == 1 && (arr[i][j] == -1 || arr[j][i] == 1)) || (arr[i][k] == -1 && arr[k][j] == -1 && (arr[i][j] == 1 || arr[j][i] == -1))) {
						cout << "-1";
						return 0;
					}
				}
				if (arr[i][j] == 0) {
					if (arr[i][k] == arr[k][j]) {
						arr[i][j] = arr[i][k];
						arr[j][i] = -arr[i][j];
					}
				}
			}
		}
	}
	for (int i = 1; i <= N; i++) {
		l = 1, r = N;
		for (int j = 1; j <= N; j++) {
			if (arr[i][j] == 1) r--;
			else if (arr[i][j] == -1) l++;
		}
		cout << l << " " << r << "\n";
	}
}

 

'algorithm > BOJ' 카테고리의 다른 글

[JS & Stack] 10828번: 스택  (0) 2023.07.19
BOJ 1486번 등산  (1) 2021.04.17
BOJ 3584번 가장 가까운 공통 조상  (0) 2021.03.31
BOJ 1103번 게임  (0) 2021.03.16
BOJ 2194번 유닛 이동시키기 C++  (0) 2020.07.05

BOJ : www.acmicpc.net/problem/3584

 

3584번: 가장 가까운 공통 조상

루트가 있는 트리(rooted tree)가 주어지고, 그 트리 상의 두 정점이 주어질 때 그들의 가장 가까운 공통 조상(Nearest Common Anscestor)은 다음과 같이 정의됩니다. 두 노드의 가장 가까운 공통 조상은, 두

www.acmicpc.net

 

루트가 있는 트리(rooted tree)가 주어지고, 그 트리 상의 두 정점이 주어질 때 그들의 가장 가까운 공통 조상(Nearest Common Anscestor)은 다음과 같이 정의됩니다.

  • 두 노드의 가장 가까운 공통 조상은, 두 노드를 모두 자손으로 가지면서 깊이가 가장 깊은(즉 두 노드에 가장 가까운) 노드를 말합니다.

예를 들어  15와 11를 모두 자손으로 갖는 노드는 4와 8이 있지만, 그 중 깊이가 가장 깊은(15와 11에 가장 가까운) 노드는 4 이므로 가장 가까운 공통 조상은 4가 됩니다.

루트가 있는 트리가 주어지고, 두 노드가 주어질 때 그 두 노드의 가장 가까운 공통 조상을 찾는 프로그램을 작성하세요

 

풀이

최근 있었던 코딩테스트에서 LCA를 기반으로 하는 문제가 출제되었는데,

지식이 없어서 풀지 못했었다.

LCA 알고리즘을 공부한 후에 3584번 문제를 풀게 되었는데,

당연히 LCA 로 풀 수 있지만, 어떤 알고리즘이든 기본 베이스가 중요하다고 생각하며, 특히 이 문제는 쿼리의 범위가 작기 때문에 이번에는 LCA 알고리즘이 아닌, 일반 dfs로 문제를 풀어보았다.

 

접근은 LCA 알고리즘과 비슷하게 

1. depth를 갖는 정보를 만들어 준 후에

2. LCA를 알고자하는 두 정점을 입력받으면 두 정점의 depth를 먼저 맞춰주고

3. depth가 동일할 때 두 정점이 다르다면, 다시 parent를 찾아 올라가면서 최소공통조상을 찾아주었다.

 

 

// LCA 알고리즘을 사용하지 않은 풀이

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;

int depth[10001];
int parent[10001];
vector<int> v[10001];

void calDepth(int now, int myparent) {

	depth[now] = depth[myparent] + 1;

	for (int i = 0; i < v[now].size(); i++) {
		int next = v[now][i];
		if (next != myparent) {
			calDepth(next, now);
		}
	}

}

int main() {
	int T; cin >> T;
	while (T--) {
		int N; cin >> N;
		for (int i = 1; i <= N; i++) v[i].clear();
		memset(parent, 0, sizeof(parent));

		for (int i = 1; i < N; i++) {
			int f, s;
			cin >> f >> s;
			v[f].push_back(s);
			parent[s] = f;
		}

		// 루트정점이 뭔지 모르기 때문에, 1~N 중 부모가 없는 정점을 찾는다
		int root;
		for (int i = 1; i <= N; i++) {
			if (parent[i] == 0) {
				root = i;
				break;
			}
		}

		// root 정점부터 depth를 계산한다
		depth[0] = -1;
		calDepth(root, 0);

		// depth 계산이 끝났으면 LCA를 찾는다
		int f, s;
		cin >> f >> s;

		// 정점 f, s의 depth가 다르다면
		if (depth[f] != depth[s]) {
			
			// 정점 f를 상향한다고 가정
			if (depth[f] < depth[s])
				swap(f, s);

			// depth를 맞춘다
			while (depth[f] > depth[s]) {
				f = parent[f];
			}
		}

		// f와 s가 서로 같은 정점이면
		if (f == s) {
			cout << f << "\n";
		}
		else {
			while (parent[f] != parent[s]) {
				f = parent[f];
				s = parent[s];
			}
			cout << parent[f] << "\n";
		}
	}
}

'algorithm > BOJ' 카테고리의 다른 글

BOJ 1486번 등산  (1) 2021.04.17
BOJ 2273번 줄 서기  (0) 2021.04.17
BOJ 1103번 게임  (0) 2021.03.16
BOJ 2194번 유닛 이동시키기 C++  (0) 2020.07.05
BOJ 14750번 Jerry and Tom  (0) 2020.04.28

www.acmicpc.net/problem/1103

 

1103번: 게임

줄에 보드의 세로 크기 N과 가로 크기 M이 주어진다. 이 값은 모두 50보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에 보드의 상태가 주어진다. 쓰여 있는 숫자는 1부터 9까지의 자연수 또는

www.acmicpc.net

문제

형택이는 1부터 9까지의 숫자와, 구멍이 있는 직사각형 보드에서 재밌는 게임을 한다.

일단 보드의 가장 왼쪽 위에 동전을 하나 올려놓는다. 그다음에 다음과 같이 동전을 움직인다.

  1. 동전이 있는 곳에 쓰여 있는 숫자 X를 본다.
  2. 위, 아래, 왼쪽, 오른쪽 방향 중에 한가지를 고른다.
  3. 동전을 위에서 고른 방향으로 X만큼 움직인다. 이때, 중간에 있는 구멍은 무시한다.

만약 동전이 구멍에 빠지거나, 보드의 바깥으로 나간다면 게임은 종료된다. 형택이는 이 재밌는 게임을 되도록이면 오래 하고 싶다.

보드의 상태가 주어졌을 때, 형택이가 최대 몇 번 동전을 움직일 수 있는지 구하는 프로그램을 작성하시오.

 

입력

줄에 보드의 세로 크기 N과 가로 크기 M이 주어진다. 이 값은 모두 50보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에 보드의 상태가 주어진다. 쓰여 있는 숫자는 1부터 9까지의 자연수 또는 H이다. 가장 왼쪽 위칸은 H가 아니다. H는 구멍이다.

출력

첫째 줄에 문제의 정답을 출력한다. 만약 형택이가 동전을 무한번 움직일 수 있다면 -1을 출력한다.

 

-------------------------------------------------------------------------------------------------------

 

오랜만에 백준을 풀면서 포스팅을 했습니다.

 

이번 문제는 난이도 골드1의 게임 문제를 풀어보겠습니다.

 

항상 그렇듯 문제를 풀기 전에 어떤 식으로 풀 것인지 접근하는 것이 중요하다고 생각합니다.

 

문제를 읽어보면 기본적으로 dfs, bfs 같은 탐색을 이용한다는 것을 유추할 수 있고,

N이 50정도면 최대한 이동할 수 있는 범위를 찾고자 할 때 매 구간 캐시값을 저장해두지 않으면 시간초과가 날 것이므로 캐시값을 저장해두는 다이나믹 프로그래밍 방식인 것을 유추할 수 있습니다.

 

dp의 차원을 정할 때는 어떤 정보가 필요할 것인지 고려해줘야 하는데,

현재 문제의 경우에는 단순히 dp[y][x] = 위치 y,x 에서 갈 수 있는 최대한의 이동거리 만을 알면 되기 때문에

3차원, 4차원까지 추가적인 배열을 사용할 필요가 없습니다.

 

이외에는 문제에서 요구하는대로 이동해주면서 가장 많이 이동할 수 있는 거리를 dp를 사용해서

문제를 해결해주시면 됩니다.

 

#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;
int N, M;
string arr[50];
ll dp[50][50];
int my[4] = { -1,1,0,0 };
int mx[4] = { 0,0,-1,1 };
bool INF = false;

ll dfs(int y, int x) {
	// 이동할 수 없는 경우 이동거리를 -1 만큼 반환해주어서 계산해준다
	if (y < 0 || y >= N || x < 0 || x >= M || arr[y][x] == 'H' || INF) return -1;

	ll& res = dp[y][x];
	// dp[y][x]가 -2 인 경우 이미 한번 방문한 곳이므로
	// 더 확인할 필요없이 무한루프가 된다
	if (res == -2) {
		INF = true;
		return 0;
	}
	// -1이 아닌 경우 dp값을 갖고 있는 것이므로 사용해주면 된다
	if (res != -1) return res;

	// 애초에 방문한 현재 위치를 -2와 같은 임의의 값으로 바꿔주어서
	// 이미 방문한 곳임을 표시해준다
	res = -2;
	ll ans = 0;

	int move = arr[y][x]-'0';

	for (int i = 0; i < 4 && !INF; i++) {
		int yy = y + (my[i]*move);
		int xx = x + (mx[i]*move);
		// 가장 많이 이동할 수 있는 거리를 찾는다
		ans = max(ans, dfs(yy, xx) + 1);
	}

	return res = ans;
}


int main() {
	cin >> N >> M;
	for (int i = 0; i < N; i++) {
		cin >> arr[i];
	}
	memset(dp, -1, sizeof(dp));
	dfs(0, 0);
	if (INF) cout << "-1";
	else cout << dp[0][0] + 1;
}

'algorithm > BOJ' 카테고리의 다른 글

BOJ 2273번 줄 서기  (0) 2021.04.17
BOJ 3584번 가장 가까운 공통 조상  (0) 2021.03.31
BOJ 2194번 유닛 이동시키기 C++  (0) 2020.07.05
BOJ 14750번 Jerry and Tom  (0) 2020.04.28
BOJ 1647번 도시 분할 계획  (0) 2020.04.11

+ Recent posts