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

개인적으로 STL 사용하면서 기억안나던 부분은 여기에 정리

 

string find

=> string().find()

발견 : index 반환

실패 : string::no position 

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

JS / Queue  (0) 2023.08.01
Binary , lower , upper searching  (0) 2020.09.24
이분 매칭(Bipartite Matching)  (0) 2020.06.22
네트워크 플로우(Maximum flow)  (0) 2020.06.22
위상 정렬(Topological Sort)  (0) 2020.06.22

programmers.co.kr/learn/courses/30/lessons/43236

 

코딩테스트 연습 - 징검다리

출발지점부터 distance만큼 떨어진 곳에 도착지점이 있습니다. 그리고 그사이에는 바위들이 놓여있습니다. 바위 중 몇 개를 제거하려고 합니다. 예를 들어, 도착지점이 25만큼 떨어져 있고, 바위가

programmers.co.kr

 

이분탐색 l4 문제인 징검다리 입니다.

이분탐색에 해당하는 문제를 풀 때는

이분탐색의 구간에 해당하는 것이 무엇인지, 이를 판단하는 조건은 무엇인지 파악하는 것이 중요합니다.

문제에서 주어졌듯이 이분탐색의 '구간'에 해당하는 것, 즉 원하는 값은

각 돌 사이의 간격이며 이 간격들 중의 최솟값을 최대로 하는 것입니다.

이를 판단하는 조건은 n개의 돌을 제거하는 것!

 

여기서 아래와 같이 생각해볼 수 있습니다.

나열된 여러 개의 돌 중에서 n개를 제거하면서 돌 사이 간격의 최솟값을 최대로 하고 싶다

=> n개의 돌을 제거한 이분탐색의 결과값인 '간격'을 알고 싶다

=> 임의의 '간격'을 기준으로 돌을 제거할 수 있을까

=> 임의의 '간격'보다 작은 '간격'에 해당하는 돌을 제거하면서 몆개를 제거할 수 있는지 확인한다

==> 만약 제거한 돌의 갯수가 n개보다 작다면?

===> 이것은 괜찮다. 일단 모든 돌의 간격을 '간격' 이상으로 유지했다는 뜻이니까

         n개가 될 수 있게 더 제거해도 된다는 의미가 된다.

==> 만약 제거한 돌의 갯수가 n개보다 크다면?

====> 이것은 안된다. 모든 돌의 간격을 원하는 '간격' 기준만큼 유지할 수 없다는 뜻이니까

=> 제거한 돌의 갯수가 n개이하라면 모든 돌의 간격은 원하는 기준값인 '간격' 보다 크거나 같아진다.

=> 최솟값의 최댓값을 찾기 위해 제거한 돌의 갯수가 n개 이하라면 기준값을 늘려보고

=> 제거한 돌의 갯수가 n개를 초과한다면 기준값을 줄여본다.

 

#include <string>
#include <vector>
#include <algorithm>
using namespace std;

bool canDelete(vector<int> v, int mid, int n){
    int deleteRock = 0;
    int leftIdx = 0;
    int rightIdx = 1;
    while(rightIdx < v.size()){
        if(deleteRock > n) break;
        int dist = v[rightIdx] - v[leftIdx];
        if(dist < mid){
            deleteRock++;
            rightIdx++;
        }
        else{
            leftIdx = rightIdx++;
        }
    }
    
    if(deleteRock <= n) return true;
    else return false;
}

int solution(int distance, vector<int> rocks, int n) {
    rocks.push_back(0);
    rocks.push_back(distance);
    sort(rocks.begin(), rocks.end());
    
    int left = 1, right = 1000000000, mid;
    int answer = left;
    
    while(left<=right){
        mid = (left+right)/2;
        if(canDelete(rocks, mid, n)) {
            left = mid+1;
            answer = max(answer, mid);
        }else right = mid-1;
    }
    
    return answer;
}

+ Recent posts