[알고리즘] 창용 마을 무리의 개수

2024. 1. 9. 00:28Algorithm/with C++

0. 문제

 

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com

 


1. 문제 이해

 

이건 인접리스트를 만들고 dfs로 connected component의 수를 세는 문제라고 생각했다.

 


2. 실패

 

가. 실패 : 입력값 범위

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

const int MAX_N = 100;
int n, m, visited[MAX_N];
vector<int> adj[MAX_N];

void dfs(int node){
    visited[node]++;

    for(int next : adj[node]){
        if(visited[next]) continue;
        dfs(next);
    }

    return;
}

int solve(){
    int cnt = 0;

    for(int i = 0; i < n; i++){
        if(visited[i]) continue;
        dfs(i);
        cnt++;
    }

    return cnt;
}

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    int T;
    cin >> T;

    for(int tc = 1; tc <= T; tc++){
        fill(&visited[0], &visited[0] + MAX_N, 0);
        for(vector<int> vec : adj)
            vec.clear();
        cin >> n >> m;

        int tmp1, tmp2;
        for(int i = 0; i<m; i++){
            cin >> tmp1 >> tmp2;
            adj[tmp1].push_back(tmp2);
            adj[tmp2].push_back(tmp1);
        }

        int ret = solve();

        cout << "#" << tc << " " << ret << '\n';
    }
    return 0;
}

입력값(사람의 번호)의 범위가 1부터 시작한다.

 

int tmp1, tmp2;
for(int i = 0; i<m; i++){
    cin >> tmp1 >> tmp2;
    adj[tmp1-1].push_back(tmp2-1);
    adj[tmp2-1].push_back(tmp1-1);
}

위와 같이 -1한다.

 


나. for(vector vec : adj)의 vec는 read-only index입니다.

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

const int MAX_N = 100;
int n, m, visited[MAX_N];
vector<int> adj[MAX_N];

void dfs(int node){
    visited[node]++;

    for(int next : adj[node]){
        if(visited[next]) continue;
        dfs(next);
    }

    return;
}

int solve(){
    int cnt = 0;

    for(int i = 0; i < n; i++){
        if(visited[i]) continue;
        dfs(i);
        cnt++;
    }

    return cnt;
}

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    int T;
    cin >> T;

    for(int tc = 1; tc <= T; tc++){
        fill(&visited[0], &visited[0] + MAX_N, 0);
        for(vector<int> vec : adj)
            vec.clear();
        cin >> n >> m;

        int tmp1, tmp2;
        for(int i = 0; i<m; i++){
            cin >> tmp1 >> tmp2;
            adj[tmp1-1].push_back(tmp2-1);
            adj[tmp2-1].push_back(tmp1-1);
        }

        int ret = solve();

        cout << "#" << tc << " " << ret << '\n';
    }
    return 0;
}

for(vector<int> vec : adj)은 제대로 초기화되지 않는다!

 

C++에서 vectorcall by value다.

 

기존의 벡터를 복사한 새로운 벡터를 비우는 것이므로 실제 adj 배열의 벡터를 비우지 않는다.

 

이 문제는 "for-each loop" 또는 "enhanced for loop"에서 발생한다.

 

각 요소를 반복적으로 접근하기에 간편하지만 해당 요소를 변경하여 원본 자체를 수정할 수는 없다.

 

이러한 점에서 vec와 같아 수정할 순 없는 요소를 "read-only index" 또는 "읽기 전용 인덱스" 라고 부르기도 한다.

 

+ 배열은 call by reference이다. for-each문에서 복사한 값이 주소값이기 때문에 문제없이 원본에 접근할 수 있다.

 


3. 성공

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

const int MAX_N = 100;
int n, m, visited[MAX_N];
vector<int> adj[MAX_N];

void dfs(int node){
    visited[node]++;

    for(int next : adj[node]){
        if(visited[next]) continue;
        dfs(next);
    }

    return;
}

int solve(){
    int cnt = 0;

    for(int i = 0; i < n; i++){
        if(visited[i]) continue;
        dfs(i);
        cnt++;
    }

    return cnt;
}

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    int T;
    cin >> T;

    for(int tc = 1; tc <= T; tc++){
        fill(&visited[0], &visited[0] + MAX_N, 0);
        for(vector<int>& vec : adj)
            vec.clear();
        cin >> n >> m;

        int tmp1, tmp2;
        for(int i = 0; i<m; i++){
            cin >> tmp1 >> tmp2;
            adj[tmp1-1].push_back(tmp2-1);
            adj[tmp2-1].push_back(tmp1-1);
        }

        int ret = solve();

        cout << "#" << tc << " " << ret << '\n';
    }
    return 0;
}

for(vector<int>& vec: adj)로 고치거나…

 

for(int i = 0; i < MAX_N; ++i) {
    visited[i] = 0;
    adj[i].clear();
}

위와 같이 초기화해야 한다.