Stunning Garbanzo
open main menu
Part of series: PS

1006 BOJ

/ 4 min read

문제

접근

평범한 DP에 수만가지의 경우의 수를 구해야 하는 작업이 혼합된 문제이다.

점화식 자체는 세우기 쉬우나 경우의 시작지점에서의 경우의 수를 구현하는 것이 상당히 까다로운 문제였다.

코드

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef const ll cll;
#define FOR(i, a, A) for (ll i = a; i < A; ++i)
#define IFOR(i, a, A) for (ll i = a; i >= A; --i)

cll N = 1e4, W = 1e4, Inf = N * 2;
ll n, w, enemies[N][2] = {}, dp[N][4][4] = {{{}}};

ll check(ll idx, ll status, ll tail) {
  if (idx >= n) {
    return 0;
  }

  ll &value = dp[idx][status][tail];
  if (value != -1) {
    return value;
  }

  value = Inf;
  switch (status) {
  case 0:
    value = min(value, check(idx + 1, 0, tail) + 2);
    if (enemies[idx][0] + enemies[idx][1] <= w) {
      value = min(value, check(idx + 1, 0, tail) + 1);
    }

    if (enemies[idx][0] + enemies[idx + 1][0] <= w) {
      if (idx + 1 != n - 1 || !(tail & (1 << 0))) {
        value = min(value, check(idx + 1, 1, tail) + 2);
      }
    }

    if (enemies[idx][1] + enemies[idx + 1][1] <= w) {
      if (idx + 1 != n - 1 || !(tail & (1 << 1))) {
        value = min(value, check(idx + 1, 2, tail) + 2);
      }
    }

    if (enemies[idx][0] + enemies[idx + 1][0] <= w &&
        enemies[idx][1] + enemies[idx + 1][1] <= w) {
      if (idx + 1 != n - 1 || tail == 0) {
        value = min(value, check(idx + 2, 0, tail) + 2);
      }
    }
    break;

  case 1:
    value = min(value, check(idx + 1, 0, tail) + 1);
    if (enemies[idx][1] + enemies[idx + 1][1] <= w) {
      if (idx + 1 != n - 1 || !(tail & (1 << 1))) {
        value = min(value, check(idx + 1, 2, tail) + 1);
      }
    }
    break;

  case 2:
    value = min(value, check(idx + 1, 0, tail) + 1);
    if (enemies[idx][0] + enemies[idx + 1][0] <= w) {
      if (idx + 1 != n - 1 || !(tail & (1 << 0))) {
        value = min(value, check(idx + 1, 1, tail) + 1);
      }
    }
    break;

  case 3:
    value = check(idx + 2, 0, tail);
    break;
  }

  return value;
}

ll solve() {
  ll result = Inf;
  cin >> n >> w;
  FOR(i, 0, 2) FOR(l, 0, n) { cin >> enemies[l][i]; }
  memset(dp, -1, sizeof(dp));
  FOR(status, 0, 4) FOR(tail, 0, 4) {
    ll &value = dp[n - 1][status][tail];
    switch (status ^ tail) {
    case 0:
      if (enemies[n - 1][0] + enemies[n - 1][1] <= w) {
        value = 1;
      } else {
        value = 2;
      }
      break;
    case 1:
    case 2:
      value = 1;
      break;
    case 3:
      value = 0;
      break;
    }
  }

  result = min(result, check(0, 0, 0));
  if (n >= 2 && enemies[0][1] + enemies[n - 1][1] <= w) {
    result = min(result, check(1, 0, 2) + 2);
    if (enemies[0][0] + enemies[n - 1][0] <= w) {
      result = min(result, check(1, 0, 3) + 2);
    }
  }
  if (n >= 2 && enemies[0][0] + enemies[n - 1][0] <= w) {
    result = min(result, check(1, 0, 1) + 2);
  }

  if (n >= 2 && enemies[0][0] + enemies[1][0] <= w &&
      enemies[0][1] + enemies[n - 1][1] <= w) {
    result = min(result, check(1, 1, 2) + 2);
  }

  if (n >= 2 && enemies[0][1] + enemies[1][1] <= w &&
      enemies[0][0] + enemies[n - 1][0] <= w) {
    result = min(result, check(1, 2, 1) + 2);
  }

  return result;
}

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

  ll t;
  cin >> t;
  while (t--) {
    cout << solve() << "\n";
  }

  return 0;
}