백준 문제

백준 17471(게리맨더링) c++

kangyuseok 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);
}