알고리즘/HackerRank_문제풀이

[PS]#The Story of a Tree

코딩하는상후니 2022. 11. 23. 09:43

 

 


 

#include <bits/stdc++.h> 
using namespace std; 
#define PII pair<int, int> 
int q, n, g, k, Count[100001]; 
bool visit[100001]; 
set<string> gmap;

int DFS(vector<vector<int>>& grid, int x) 
{ 
	if (visit[x]) return 0; 
	visit[x] = true; 
	int res = 0; 
	for (int i = 0; i < grid[x].size(); ++i) 
	{ 
		if (visit[grid[x][i]]) continue; 
		string str = to_string(x) + to_string(grid[x][i]); 
		if (gmap.find(str) != gmap.end()) ++res; 
		res += DFS(grid, grid[x][i]); 
	} 
	return res; 
}

void LoopDFS(vector<vector<int>>& grid, int idx, int beforeIdx, int cnt) 
{ 
	if (visit[idx]) return; 
	visit[idx] = true; 
	int curCnt = cnt; 
	if (beforeIdx != -1) 
	{ 
		string beforeStr = to_string(beforeIdx) + to_string(idx); 
		string Str = to_string(idx) + to_string(beforeIdx); 
		if (gmap.find(beforeStr) != gmap.end()) curCnt--; 
		if (gmap.find(Str) != gmap.end()) curCnt++; 
	} 
	Count[idx] = curCnt; 
	for (int i = 0; i < grid[idx].size(); ++i) 
	{ 
		if (visit[grid[idx][i]]) continue; 
		LoopDFS(grid, grid[idx][i], idx, curCnt); 
	} 
}

PII Calc(int x, int y) 
{ 
	int X = x, Y = y; 
	for (int i = 2; i <= X;) 
	{ 
		if (X % i == 0 && Y % i == 0) 
		{ 
			X /= i; Y /= i; 
			i = 2; 
		} 
		else ++i; 
	} 
	return PII(X, Y); 
}

string storyOfATree(vector<vector<int>> edges, vector<PII> guesses) 
{ 
	vector<vector<int>> grid(n + 1); 
	for (int j = 1; j <= n; ++j) 
	{ 
		for (int i = 0; i < edges[j].size(); ++i) 
		{ 
			grid[j].push_back(edges[j][i]); 
			grid[edges[j][i]].push_back(j); 
		} 
	} 
    
	gmap.clear(); 
	int gsz = guesses.size(); 
	for (int i = 0; i < gsz; ++i) 
	{ 
		int f = guesses[i].first, s = guesses[i].second; 
		string str = to_string(f) + to_string(s); 
		gmap.insert(str); 
	} 
    
	memset(visit, false, sizeof(visit)); 
	int initCnt = DFS(grid, 1); 
    
	memset(visit, false, sizeof(visit)); 
	memset(Count, 0, sizeof(Count)); 
	LoopDFS(grid, 1, -1, initCnt); 
    
	int answer = 0; 
	for (int i = 1; i <= n; ++i) 
	{ 
		if (Count[i] >= k) ++answer; 
	} 
    
	PII cc = Calc(answer, n); 
	if (cc.first == 0) cc.second = 1; 
	string answerStr = to_string(cc.first); 
	answerStr.append("/"); 
	answerStr += to_string(cc.second); 
	return answerStr; 
}
 

* 문제 풀이 과정

 
문제를 간단히 요약하자면,
모든 노드는 루트가 될 수 있다.
임의의 노드가 루트일 때, 주어진 데이터 guesses 를 만족하는 경로의 갯수를 point 라고 한다.
k >= point 라면 해당 노드는 참.
( 참이 되는 노드의 수 / 모든 노드의 수 ) 인 값을 문자로 출력하자.
 
모든 경우의 수를 찾는 것은 어렵지 않다.
DFS 를 이용해 모든 노드를 돌아가며 Alice 가 추측한 guesses 벡터에 포함된 경로가 존재하는지 확인할 수 있다.
 
문제는 '시간 초과' 이다. 어떻게 줄일 수 있을까 ??
시간 초과 문제에서 중요하게 볼 요점은 논리적으로 순서를 바꾸거나 조합하는 것이 아니라
등장하는 주요 for 문을 어떤 알고리즘이나 방식으로 없애거나 줄여야만 한다.
로직을 아예 바꾸어야한다는 것을 인지해야한다.
 
다른 말로 하면 안타깝게도 시간 초과 문제는
요약할 수 있는 방식을 못 찾으면 풀지 못한다.
 
하지만 풀어야만 하는 상황에서 우리는 어디서 단서를 찾을 수 있을까 ??
우선, 주어진 데이터를 보아야한다.
또, 우리가 출력해야하는 결과 값에 필요한 과정들을 파악해야한다.
 
이 문제를 빗대어 살펴보자.
 
주어진 데이터 : 양방향 경로가 담긴 데이터, 방향을 예측하는 Alice 의 추측 데이터.
결과 값에 필요한 요소 : 모든 노드가 루트인 경우마다 Alice 추측한 데이터 확인.
( 모든 노드는 서로 연결되어 있다. )
 
1. 모든 경로를 돈다. (for)
1. DFS 를 통해 해당 경로를 확인하고 계산. ( for )
=> 중첩 for 문. (총 n 제곱, O(n2) )
 
위를 토대로,
DFS 로 모든 노드를 거치면서 루트로 설정할 수 있는 것과
이전 노드가 루트인 상황에서 현재 노드가 루트인 상황으로 올 때의 차이점만 구별한다면
모든 경로를 도는 for 문을 지울 수 있는 사실을 알아내야만 한다.
 
 
정리하면,
DFS 로 들어간 상황의 idx 가 root 가 된다.
이전의 idx, 지금의 idx 를 비교하며
루트가 바뀌어 세어야할 경로가 없어지는 경우, 경로가 새로 생기는 경우를 고려한다.
 
1. 임의의 노드를 기준으로 ( 코드에서는 '1' 노드 ) 초기의 값 ( Alice 의 예측이 맞은 총 갯수 )을 구한다.
2. DFS 에 idx, 이전의 idx, 초기의 값을 파라미터로 돌리면서 앞서 말한 경루를 고려해 값을 구해나간다.
=> 중첩이 아니다. ( O(n) )
 
추가적으로,
노드 n 의 개수가 너무 많기 때문에 배열 인덱스를 이용해서 ( O(1) ) 해당 연결 여부를 찾을 수 없다.
string 을 키로 하는 set 컨테이너를 이용해서 O( log(n) ) 으로 찾게 구현하였다.
 
마지막으로 값을 출력할 때,
분자, 분모가 서로 약분될 수 있으므로 Calc 함수를 구현해 따로 계산해주었다.
 
 
 
 

* 해결 과정 문제점

 
시간 초과를 해결할 수 있는 방법을 찾지 못해 시간이 많이 소모됐다.
계속 시간 복잡도가 같은 방식으로만 로직을 바꾼 것이 큰 요인으로 보인다.
 
 
 
 
 
 

* 또 다른 해결 방법

 
서로 약분되어질 최대공약수를 구하는 gcd 방법을 이용하는 것.

 

int GCD(int a, int b)
{
	if (b == 0) return a;
	return GCD(b, a % b);
}

 

 

 

 

 

 

 

 

'알고리즘 > HackerRank_문제풀이' 카테고리의 다른 글

[PS]#Roads and Libraries  (0) 2022.12.03
[PS]#Lily's Homework  (0) 2022.11.20
[PS]#Bear and Steady Gene  (0) 2022.11.20
[PS]#Ice Cream Parlor  (0) 2022.11.20
[PS]#Gridland Metro  (0) 2022.11.17