ferinの競プロ帳

競プロについてのメモ

AOJ0550 お菓子の分割

問題ページ
お菓子の分割 | Aizu Online Judge

解法

いかにもDPの雰囲気があるのでDPを考える。AくんとBくんでお菓子を分け合うとして dp[k][i][j] = (k番目まででAくんがi個取って最後に取ったのがAくんならj=0、Bくんならj=1) とおいてDPをする。遷移に必要なのはk-1のときのDPの値だけなので配列は2つ分持っておけば十分。

//#define __USE_MINGW_ANSI_STDIO 0
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef vector<int> VI;
typedef vector<VI> VVI;
typedef vector<ll> VL;
typedef vector<VL> VVL;
typedef pair<int, int> PII;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()
#define IN(a, b, x) (a<=x&&x<b)
#define MP make_pair
#define PB push_back
const int INF = (1LL<<30);
const ll LLINF = (1LL<<60);
const double PI = 3.14159265359;
const double EPS = 1e-12;
const int MOD = 1000000007;
//#define int ll

template <typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template <typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }

int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};

int t[10010], dp[2][10010][2];
signed main(void)
{
  int n;
  cin >> n;
  REP(i, n-1) cin >> t[i];

  memset(dp, 0x3f, sizeof(dp));
  dp[0][1][0] = dp[0][0][1] = t[0];
  // k=0ならA、k=1ならB
  int cur = 0, nxt = 1;
  FOR(i, 1, n) {
    REP(j, n) REP(k, 2) dp[nxt][j][k] = 0x3f3f3f3f;
    REP(j, n+1) {
      REP(k, 2) {
        if(dp[cur][j][k] == 0x3f3f3f3f) continue;
        if(k == 0) {
          // Aに選ぶ
          chmin(dp[nxt][j+1][0], dp[cur][j][k] - t[i-1] + t[i]);
          // Bに選ぶ
          chmin(dp[nxt][j][1], dp[cur][j][k] + t[i]);
        } else {
          chmin(dp[nxt][j+1][0], dp[cur][j][k] + t[i]);
          chmin(dp[nxt][j][1], dp[cur][j][k] - t[i-1] + t[i]);
        }
      }
    }
    cur = !cur, nxt = !nxt;
  }

  cout << min(dp[cur][n/2][0], dp[cur][n/2][1]) << endl;

  return 0;
}