본문 바로가기

백준/탐색

백준 4160 - 이혼

728x90
728x90

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

힌트

더보기

브루트 포스를 시행하면 $3^{24}$만큼 경우의 수가 있다.

 

밋 인 더 미들을 통해 $O(3^{12})$로 풀 수 있다.

풀이

더보기

다음 3가지를 한 번에 고려해보자.

 

잭이 가지는 집의 가치

질이 가지는 집의 가치

판매하는 집의 가치

 

3가지를 각각 따져서 브루트 포스로 잭의 집 가치와 질의 집 가치가 같은 경우를 찾으면 $3^{24}$만큼 경우의 수가 나오고 이는 매우 커서 30초라는 제한 시간 안에 어림도 없음을 알 수 있다.

 

밋 인 더 미들을 기법을 사용해 주어진 집을 2개의 부분 집합으로 쪼개자. 그러면 각 집합의 크기는 최대 12가 된다. 이때 우리는 다음을 구할 것이다.

 

분할된 집합을 각각 $s_{1}$, $s_{2}$라고 하자. $s_{x}$에서 집을 배분하는 i번째 조합 $s_{x, i}$에 대해 잭의 집 가치와 질의 집 가치의 차이를 $s_{x, i}(|value|)$라고 하고 판매하는 집의 가치를 $s_{x, i}(cost)$라고 하면 $s_{1, i}(|value|) = s_{2, j}(|value|)$ 일 때 $min(s_{1, i}(cost) + s_{2, j}(cost)) (i, j \leq 3^{12})$를 구하는 문제로 바뀐다.

 

각 조합마다 판매하는 집의 가치의 최솟값을 찾으면 제한 시간 안에 여유롭게 정답을 받을 수 있다. 자세한 구현은 밑을 참고한다.

 

전체 코드

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,avx,avx2,fma")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")

#include <bits/stdc++.h>
#include <ext/rope>

using namespace std;
using namespace __gnu_cxx;
using ll = long long;
using pii = pair<int, int>;

int n;
int tb;
int sum;
int ar[24];
int ss1[12];
int ss2[12];
int ternary[24];
vector<pii> v1, v2;
int l, r;

void backtrack(int idx, int limit, int type)
{
  if (idx == limit)
  {
    int jack = 0, jill = 0;
    int sell = 0;
    for (int i = 0; i < limit; i++)
    {
      if (ternary[i] == 1)
      {
        if (type == 1) jack += ss1[i];
        else jack += ss2[i];
      }
      else if (ternary[i] == 2)
      {
        if (type == 1) jill += ss1[i];
        else jill += ss2[i];
      }
      else
      {
        if (type == 1) sell += ss1[i];
        else sell += ss2[i];
      }
    }

    if (type == 1)
    {
      v1.push_back({abs(jack - jill), sell});
    }
    else
    {
      v2.push_back({abs(jack - jill), sell});
    }
  }
  else
  {
    for (int i = 1; i <= 3; i++)
    {
      ternary[idx] = i;
      backtrack(idx + 1, limit, type);
    }
    ternary[idx] = 0;
  }
}

void solve()
{
  v1.clear();
  v2.clear();
  l = r = 0;
  tb = (1 << n) - 1;
  int bc = __builtin_popcount(tb) / 2;
  int ans = 1e9;

  for (int i = 0; i < n; i++)
  {
    if (l < bc)
    {
      cin >> ss1[l++];
    }
    else
    {
      cin >> ss2[r++];
    }
  }

  backtrack(0, l, 1);
  backtrack(0, r, 2);
  sort(v1.begin(), v1.end());
  sort(v2.begin(), v2.end());

  for (int i = 0; i < v1.size(); i++)
  {
    auto cur = v1[i];
    int idxl = lower_bound(v2.begin(), v2.end(), cur, [](const pii &l, const pii &r) {
      return l.first < r.first;
    }) - v2.begin();
    int idxr = upper_bound(v2.begin(), v2.end(), cur, [](const pii &l, const pii &r) {
      return l.first < r.first;
    }) - v2.begin();

    for (int j = idxl; j < idxr; j++)
    {
      ans = min(ans, cur.second + v2[j].second);
    }
  }

  cout << ans << '\n';
}

int main()
{
  cin.tie(nullptr);
  ios::sync_with_stdio(false);

  cin >> n;

  while (n != 0)
  {
    solve();
    cin >> n;
  }
}

 

제출 기록

 

728x90
728x90

'백준 > 탐색' 카테고리의 다른 글

[KOI] 백준 2503 - 숫자 야구  (0) 2022.06.22
[KOI] 백준 2502 - 떡 먹는 호랑이  (0) 2022.05.26
[ICPC] 백준 20047 - 동전 옮기기  (0) 2021.10.09
백준 2208 - 보석 줍기  (0) 2021.01.27
백준 1208 - 부분수열의 합 2  (0) 2021.01.23