ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 백준 17471(게리맨더링) c++
    백준 문제 2023. 3. 30. 19:27
    728x90

    문제

     

    17471번: 게리맨더링

    선거구를 [1, 4], [2, 3, 5, 6]으로 나누면 각 선거구의 인구는 9, 8이 된다. 인구 차이는 1이고, 이 값보다 더 작은 값으로 선거구를 나눌 수는 없다.

    www.acmicpc.net

    n개의 노드와 간선정보가 주어지면, 위와 같이 빨간색 부분과 파란색 부분을 나누어, 빨간색 부분의 합과 파란색 부분의 합의 최솟값을

    구하는 문제입니다.

    물론 각 노드마다 값은 주어집니다.

    또한 같은 부분에 속하는 모든 노드는 서로 연결되어 있어야 합니다.

    1) 일단 빨간색, 파란색 부분을 나누는 경우의 수, 즉 조합을 생각해야 합니다.

    2) 부분을 나눴으면, 나눈 부분들이 실제로 가능한지 판별해야 합니다.

       2-1) 부분은 최소 1개의 노드를 가져야합니다.

       2-2) 부분에 속하는 노드들은 서로 이어져 있어야 합니다.

    위의 작성한 순서대로 코드를 작성해 보았습니다.

     

    1)

    void dfs(int start, int cnt)
    {
        if (cnt >= 1)
            if (check())
            {
                cal();
            }
    
        for (int i = start; i <= n; i++)
        { // 조합 부분
            if (team[i])
                continue;
            team[i] = true;
            dfs(i, cnt + 1);
            team[i] = false;
        }
    }

    start는 현재 내가 가리키고 있는 노드 번호이고, cnt는 현재 내가 팀으로 고른 인원 수 입니다.

    따라서 처음 if문으로 들어가려면, cnt가 최소 1이상이어야 합니다.

     

    2-1)

    bool check()
    {
        vector<int> A, B;
        for (int i = 1; i <= n; i++)
        { // 팀에 따라 각각 A, B에 넣어주기
            if (team[i])
                A.push_back(i);
            else
                B.push_back(i);
        }
        if (A.size() == 0 || B.size() == 0)
            return false; // 0명 뽑은 경우
    
        if (!connection(A, true))
            return false;
        if (!connection(B, false))
            return false;
        return true;
    }

    A부분, B부분으로 나누어 벡터에 넣어줍니다.

    각각의 벡터의 사이즈가 0이면 부분에 노드가 0개이므로 탈락입니다.

     

    2-2)

    bool connection(vector<int> V, bool b)
    { // 각 팀의 노드들이 연결되어 있는지
        memset(Visit, false, sizeof(Visit));
        queue<int> q;
        q.push(V[0]);
        Visit[V[0]] = true;
        int cnt = 1;
        while (!q.empty())
        {
            int node = q.front();
            q.pop();
            for (auto nxt : v[node])
            {
                if (team[nxt] == b && !Visit[nxt])
                {
                    Visit[nxt] = true;
                    cnt++;
                    q.push(nxt);
                }
            }
        }
        return V.size() == cnt;
    }

    bfs를 이용해 각 벡터에 넣어진 노드들이 서로 이어져 있는지 확인합니다.

    void cal()
    {
        int a = 0, b = 0;
        int d;
        for (int i = 1; i <= n; i++)
        {
            if (team[i])
                a += arr[i];
            else
                b += arr[i];
        }
        d = abs(a - b);
        ans = min(ans, d);
    }

    저 위에 모든 조건을 만족하면 이제 각 노드끼리의 차이의 최솟값을 계산해주어야 합니다.


    전체 코드입니다.

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <sstream>
    #include <algorithm>
    #include <vector>
    #include <stack>
    #include <queue>
    #include <map>
    #include <math.h>
    #define INF 2000000000
    using namespace std;
    using ll = long long;
    int arr[11];
    vector<int> v[11];
    bool team[11];
    int n;
    int ans = INF;
    void dfs(int start, int cnt);
    bool check();
    bool connection(vector<int> V, bool b);
    void cal();
    bool Visit[11];
    int main()
    {
        ios_base::sync_with_stdio(false), cin.tie(NULL), cout.tie(NULL);
        cin >> n;
        for (int i = 1; i <= n; i++)
            cin >> arr[i];
    
        for (int i = 1; i <= n; i++)
        {
            int a;
            cin >> a;
            for (int j = 0; j < a; j++)
            {
                int b;
                cin >> b;
                v[i].push_back(b);
            }
        }
    
        dfs(1, 0);
        if (ans == INF)
            cout << -1;
        else
            cout << ans;
    
        return 0;
    }
    void dfs(int start, int cnt)
    {
        if (cnt >= 1)
            if (check())
            {
                cal();
            }
    
        for (int i = start; i <= n; i++)
        { // 조합 부분
            if (team[i])
                continue;
            team[i] = true;
            dfs(i, cnt + 1);
            team[i] = false;
        }
    }
    bool check()
    {
        vector<int> A, B;
        for (int i = 1; i <= n; i++)
        { // 팀에 따라 각각 A, B에 넣어주기
            if (team[i])
                A.push_back(i);
            else
                B.push_back(i);
        }
        if (A.size() == 0 || B.size() == 0)
            return false; // 0명 뽑은 경우
    
        if (!connection(A, true))
            return false;
        if (!connection(B, false))
            return false;
        return true;
    }
    bool connection(vector<int> V, bool b)
    { // 각 팀의 노드들이 연결되어 있는지
        memset(Visit, false, sizeof(Visit));
        queue<int> q;
        q.push(V[0]);
        Visit[V[0]] = true;
        int cnt = 1;
        while (!q.empty())
        {
            int node = q.front();
            q.pop();
            for (auto nxt : v[node])
            {
                if (team[nxt] == b && !Visit[nxt])
                {
                    Visit[nxt] = true;
                    cnt++;
                    q.push(nxt);
                }
            }
        }
        return V.size() == cnt;
    }
    void cal()
    {
        int a = 0, b = 0;
        int d;
        for (int i = 1; i <= n; i++)
        {
            if (team[i])
                a += arr[i];
            else
                b += arr[i];
        }
        d = abs(a - b);
        ans = min(ans, d);
    }

    '백준 문제' 카테고리의 다른 글

    백준 2567(색종이2) JAVA  (0) 2023.07.11
    백준 2018(수들의 합 5) JAVA <투 포인터>  (0) 2023.07.10
    백준 2110(공유기 설치) c++  (0) 2023.03.28
    백준 5904(Moo 게임) c++  (0) 2023.03.24
    백준 2133(타일 채우기) c++  (0) 2023.03.22
Designed by Tistory.