코테 노트/백준

백준 14889 스타트와 링크 C++

화요밍 2021. 1. 6. 16:45
728x90
반응형

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

 

14889번: 스타트와 링크

예제 2의 경우에 (1, 3, 6), (2, 4, 5)로 팀을 나누면 되고, 예제 3의 경우에는 (1, 2, 4, 5), (3, 6, 7, 8)로 팀을 나누면 된다.

www.acmicpc.net

최종 코드

 

hwayeon351/BEAKJOON-Algorithms

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

github.com

//
//  main.cpp
//  BJ14889
//
//  Created by Hwayeon on 2021/07/07.
//

#include <iostream>
#include <cmath>
using namespace std;

int N;
int S[20][20] = {0, };
int visit[20] = {0,};
int answer = 987654321;

void dfs(int idx, int start_n){
    if(idx >= N) return;
    if(start_n == N/2){
        //능력치 구하기 -> O(n^2)
        int start_sum = 0;
        int link_sum = 0;
        for(int i=0; i<N; i++){
            for(int j=0; j<N; j++){
            	//스타트 팀원은 visit[idx] = 1이므로, i,j가 모두 스타트 팀원이면 visit[i]+visit[j] = 2이다.
                if(visit[i]+visit[j]==2) start_sum += S[i][j];
                
                //링크 팀원은 visit[idx] = 0이므로, i,j가 모두 링크 팀원이면  visit[i]+visit[j] = 0이다.
                //i=j인 경우, S[i][j] = 0이므로, 더해줘도 무관하다.
                else if(visit[i]+visit[j]==0) link_sum += S[i][j];
            }
        }
        int differ = abs(start_sum-link_sum);
        //능력치 차가 answer보다 작은 경우, answer 값을 갱신해준다.
        if(differ < answer) answer = differ;
        return;
    }
    
    //스타트 팀원 뽑기 -> 조합 O(2^n)
    for(int i=idx; i<N; i++){
        if(start_n+1<=N/2){
            visit[i] = 1;
            dfs(i+1, start_n+1);
            visit[i] = 0;
        }
    }
}
    
int main(int argc, const char * argv[]) {
    cin >> N;
    for(int i=0; i<N; i++){
        for(int j=0; j<N; j++){
            cin >> S[i][j];
        }
    }
    dfs(0, 0);
    cout << answer << endl;
    return 0;
}
#시간복잡도 = O(2^n*n^2) 공간복잡도 = O(n)

최종 풀이

풀이 시간 1시간 1분

//
//  main.cpp
//  BJ14889
//
//  Created by Hwayeon on 2021/07/07.
//

#include <iostream>
#include <cmath>
using namespace std;

int N;
int S[20][20] = {0, };
int visit[20] = {0,};
int answer = 987654321;

void dfs(int idx, int start_n){
    if(idx >= N) return;
    //팀 배정 완료
    if(start_n == N/2){
        //능력치 구하기
        int start_sum = 0;
        int link_sum = 0;
        for(int i=0; i<N; i++){
            for(int j=0; j<N; j++){
                if(visit[i]+visit[j]==2) start_sum += S[i][j];
                else if(visit[i]+visit[j]==0) link_sum += S[i][j];
            }
        }
        int differ = abs(start_sum-link_sum);
        if(differ < answer) answer = differ;
        return;
    }
    
    //팀원 뽑기
    for(int i=idx; i<N; i++){
        if(start_n+1<=N/2){
            visit[i] = 1;
            dfs(i+1, start_n+1);
            visit[i] = 0;
        }
    }
}
    
int main(int argc, const char * argv[]) {
    cin >> N;
    for(int i=0; i<N; i++){
        for(int j=0; j<N; j++){
            cin >> S[i][j];
        }
    }
    dfs(0, 0);
    cout << answer << endl;
    return 0;
}

 


이전 풀이

풀이 시간  1시간 6분

//
//  main.cpp
//  BJ14889
//
//  Created by Hwayeon on 2021/01/06.
//

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

int N, start_s, link_s;
vector<vector<int> > s;
int min_ds = 987654321;
vector<int> start_team, link_team;
bool visit[20] = {false,};

void get_ds(){
    link_team.clear();
    start_s = 0;
    link_s = 0;
    for(int i=0; i<N; i++){
        if(!visit[i]) link_team.push_back(i);
    }
    for(int i=0; i<N/2; i++){
        for(int j=0; j<N/2; j++){
            start_s += s[start_team[i]][start_team[j]];
            link_s += s[link_team[i]][link_team[j]];
        }
    }
    if(abs(start_s-link_s) < min_ds) min_ds = abs(start_s-link_s);
}

void make_start_team(int idx){
    if(start_team.size() == N/2) {
        get_ds();
        return;
    }
    int n;
    for(n=idx; n<N; n++){
        if(visit[n]) continue;
        visit[n] = true;
        start_team.push_back(n);
        make_start_team(n);
        visit[n] = false;
        start_team.pop_back();
    }
}

int main(int argc, const char * argv[]) {
    cin >> N;
    vector<int> row;
    for(int i=0; i<N; i++){
        row.clear();
        for(int j=0; j<N; j++){
            int s_num;
            cin >> s_num;
            row.push_back(s_num);
        }
        s.push_back(row);
    }
    make_start_team(0);
    cout << min_ds << endl;

    return 0;
}

 

1. 완전 탐색 > DFS

먼저, start_team과 link_team 각각 N/2명의 팀원을 구성해야 한다.

나는 start_team을 먼저 구성하고, 나머지 팀원을 link_team에 배정했다. (문제에서 N은 항상 짝수로 주어진다.)

 팀을 구성하는 방법은 DFS를 활용했다.

새 팀원을 뽑으면, 이를 구분하기 위해 visit[n] = true로 해주고 재귀를 통해 start_team이 N/2가 될 때까지 팀원을 뽑아준다.

재귀 이후에 또 다른 조합을 만들기 위해 visit[n] = false와 start_team/pop_back() 하여 뽑은 팀원을 다시 빼주도록 한다.

 start_team 구성이 끝나면 link_team을 구성하고 팀 능력치 차이를 계산하는 get_ds() 함수로 넘어가며 탐색을 종료한다.

 

 

 

 

 

 

2. 팀 능력치 계산

 팀 능력치를 계산할 때의 포인트는 문제에서 주어지는 행렬이 S(i,i)인 경우에는 0 이라는 것이다.

따라서, 2중 for문을 사용하여 더한 후에 abs를 이용하여 start_s와 link_s 값의 차이를 계산할 수 있다.

 

 


참고

 

[알고리즘] BFS와 DFS

 BFS와 DFS는 가능한 모든 경우의 수를 탐색하는 완전 탐색 알고리즘이다. BFS는 Breadth-First Search로 너비 우선 탐색을 의미하고 자료구조는 Queue를 사용한다. DFS는 Depth-First Search로 깊이 우선 탐색..

hwayomingdlog.tistory.com

 

728x90
반응형

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

백준 13460 구슬 탈출 2 C++  (3) 2021.01.18
백준 14888 연산자 끼워넣기 C++  (0) 2021.01.17
백준 14890 경사로 C++  (0) 2021.01.14
백준 14502 연구소 C++  (0) 2021.01.11
백준 14503 로봇 청소기 C++  (0) 2021.01.11