코테 노트/백준

백준 1058 친구 C++

화요밍 2021. 8. 26. 12:39
728x90
반응형

https://www.acmicpc.net/problem/1058

 

1058번: 친구

지민이는 세계에서 가장 유명한 사람이 누구인지 궁금해졌다. 가장 유명한 사람을 구하는 방법은 각 사람의 2-친구를 구하면 된다. 어떤 사람 A가 또다른 사람 B의 2-친구가 되기 위해선, 두 사람

www.acmicpc.net

 

최종 코드

 

GitHub - hwayeon351/BEAKJOON-Algorithms: 백준 알고리즘 소스 코드 모음

백준 알고리즘 소스 코드 모음. Contribute to hwayeon351/BEAKJOON-Algorithms development by creating an account on GitHub.

github.com

//
//  main.cpp
//  BJ1058 친구
//
//  Created by Hwayeon on 2021/08/26.
//

#include <iostream>
#include <queue>
#include <string.h>
using namespace std;

int max_friends = 0;
int N;
char relationship[50][50] = {'N'};
int visit[50] = {0, };

void bfs(int v){
    memset(visit, 0, sizeof(visit));
    queue<pair<int,int>> q;
    q.push({v, 0});
    visit[v] = 1;
    int friends = 0;
    int depth = 0;
    while(!q.empty()){
        int now_v = q.front().first;
        int depth = q.front().second;
        q.pop();
        if(depth > 1) continue;
        for(int i=0; i<N; i++){
            if(relationship[now_v][i] == 'Y' && visit[i] == 0){
                visit[i] = 1;
                friends++;
                q.push({i, depth+1});
            }
        }
    }
    if(friends > max_friends) max_friends = friends;
}

int main(int argc, const char * argv[]) {
    cin >> N;
    for(int i=0; i<N; i++){
        for(int j=0; j<N; j++){
            cin >> relationship[i][j];
        }
    }
    for(int i=0; i<N; i++){
        bfs(i);
    }
    cout << max_friends << endl;
    return 0;
}

풀이 과정

 친구 관계를 나타내는 입력이 N*N으로 주어진다. 즉, 인접 행렬 방식으로 그래프의 연결 관계를 표현한 입력이 주어진다는 것을 알 수 있다.

이 문제의 핵심은 A의 2-친구는 결국 A와 거리 2이내에 연결되어 있는 노드의 수라는 것이다.

이를 고려해서 각 노드별로 최대 깊이가 2인 노드까지만 방문하면서 친구 수를 카운팅 해주면 된다. BFS와 DFS 알고리즘을 활용할 수 있다는 것을 짐작할 수 있다.

하지만 실제로 두 가지 모두 적용해보니 DFS 방법으로는 풀 수 없는 경우가 있었다.(이에 대해서는 아래에 설명하도록 하겠다.)

 

 따라서 BFS 알고리즘을 활용하여 문제를 해결했고 코드는 다음과 같다.

풀이 시간 37분

#include <iostream>
#include <queue>
#include <string.h>
using namespace std;

int max_friends = 0;
int N;
//입력에 주어지는 친구 관계를 담는 배열
char relationship[50][50] = {'N'};
//이전에 카운팅한 친구를 재 탐색하지 않도록 방문 처리를 하기 위한 배열
int visit[50] = {0, };

void bfs(int v){
	//사람 v의 친구 수를 체크하기 위해 visit 배열을 초기화
    memset(visit, 0, sizeof(visit));
    
    //{친구, v와의 관계 깊이}
    //v와의 관계 깊이 = 시작 노드와의 거리, 2 이하이어야 2-친구이다
    queue<pair<int,int>> q;
    
    //BFS 탐색을 시작하기 위해 시작 노드는 v, 관계 깊이는 0, visit[시작 노드] = 1로 셋팅한다
    q.push({v, 0});
    visit[v] = 1;
    
    //사람 v의 친구 수
    int friends = 0;
    while(!q.empty()){
        int now_v = q.front().first;
        int depth = q.front().second;
        q.pop();
        //depth가 2 이상이라면 더이상 탐색하지 않는다
        if(depth > 1) continue;
       	//친구 탐색
        for(int i=0; i<N; i++){
        	//현재 노드 now_v에 연결되어 있는 노드이면서 이전에 방문하지 않은 노드인 경우, friends를 카운팅
            if(relationship[now_v][i] == 'Y' && visit[i] == 0){
                visit[i] = 1;
                friends++;
                q.push({i, depth+1});
            }
        }
    }
    //사람 v의 친구수가 이전 사람들의 친구수보다 많은 경우, max_friends를 갱신한다
    if(friends > max_friends) max_friends = friends;
}

int main(int argc, const char * argv[]) {
    cin >> N;
    for(int i=0; i<N; i++){
        for(int j=0; j<N; j++){
            cin >> relationship[i][j];
        }
    }
    //각 사람별 친구 수를 구한다
    for(int i=0; i<N; i++){
        bfs(i);
    }
    cout << max_friends << endl;
    return 0;
}
//시간복잡도 = O(V(V+E)) = O(V^2), 공간복잡도 = O(V^2)

오답 노트

결론: 예외 상황이 발생하므로 DFS를 적용할 수 없다.

 BFS로 문제를 해결한 후 DFS로도 문제를 풀어보기 위해 DFS를 적용한 코드를 짜봤다.

#include <iostream>
#include <string.h>
using namespace std;

int N;
char relationship[50][50] = {'N',};
int max_friends = 0;
int visit[50] = {0,};
int friends = 0;

void dfs(int v, int depth){
	//현재 친구수가 max_friends보다 많다면 max_friends를 갱신한다
    if(friends > max_friends) max_friends = friends;
    
    //시작 노드로부터 거리가 2 이상인 경우, 더 이상 탐색하지 않는다
    if(depth > 1) return;
    
    //친구 탐색
    for(int i=0; i<N; i++){
    	//이전에 방문하지 않았고 노드 v와 연결되어 있는 경우, 친구수를 카운팅 한다
        if(visit[i] == 0 && relationship[v][i] == 'Y'){
            visit[i] = 1;
            friends++;
            dfs(i, depth+1);
        }
    }
}

int main(int argc, const char * argv[]) {
    cin >> N;
    for(int i=0; i<N; i++){
        for(int j=0; j<N; j++){
            cin >> relationship[i][j];
        }
    }
    //각 사람에 대하여 친구 수를 구한다
    for(int i=0; i<N; i++){
    	//visit 함수 초기화
        memset(visit, 0, sizeof(visit));
        //친구수 0으로 초기화, 현재 시작 노드 방문 처리
        friends = 0;
        visit[i] = 1;
        dfs(i, 0);
    }
    cout << max_friends << endl;
    return 0;
}

코드를 짜면서는 무난히 통과할 것이라고 생각했다. 하지만 다음과 같은 테스트케이스 같은 경우, 정답을 출력하지 못한다.

 

  • 테스트 케이스

6

 

NYYNYN

YNYNNN

YYNYNN

NNYNNN

YNNNNY

NNNNYN

 

  • 출력

 

 실제로 그래프를 그려보면 0번의 친구는 0 -> 1 -> 2 -> 3-> 4-> 5로 탐색되어야 하는데, 위의 출력 결과를 보면 3은 탐색이 이뤄지지 않는다.

그 이유는 당연하다!

0 -> 1 -> 2 까지 탐색하면 현재 visit = [1, 1, 1, 0, 0, 0]이고, dfs(2, 2) 호출에서 depth가 2이기 때문에 더 탐색이 이뤄지지 않고 끝난다.

그러면 그다음 4->5를 방문하고 0번의 친구는 총 4명으로 출력이 될 것이다.

즉, 2번 친구를 1번의 친구로 탐색을 했기 때문에 방문처리가 되어서, 0 -> 2 -> 3의 경우를 탐색하지 못하는 것이다.

따라서 이와 같은 예외에 있어서 탐색이 이뤄지지 않을 노드가 발생하기 때문에, BFS 알고리즘을 활용하는 것이 적절하다.

728x90
반응형

'코테 노트 > 백준' 카테고리의 다른 글

백준 1303 전쟁 - 전투 C++  (0) 2021.08.30
백준 13565 침투 C++  (0) 2021.08.30
백준 3184 양 C++  (0) 2021.08.24
백준 2210 숫자판 점프 Python3  (0) 2021.08.22
백준 2251 물통 Python 3  (0) 2021.08.20