魔法少女さやかちゃん (AOJ 2312)

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2312

問題

解法

DPで解く。大きい数値は隣接して置いた方が良いので、一番大きい数値を置いた後に、残りの数値を大きい順に左端か右端に順々に置いていけば良い。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <math.h>
#include <assert.h>
#include <vector>

using namespace std;
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
static const double EPS = 1e-9;
static const double PI = acos(-1.0);

#define REP(i, n) for (int i = 0; i < (int)(n); i++)
#define FOR(i, s, n) for (int i = (s); i < (int)(n); i++)
#define FOREQ(i, s, n) for (int i = (s); i <= (int)(n); i++)
#define FORIT(it, c) for (__typeof((c).begin())it = (c).begin(); it != (c).end(); it++)
#define MEMSET(v, h) memset((v), h, sizeof(v))

int n, m, L;
int card[2010];
int seq[100010];
ll sum[100010];
ll memo[2010][2010];

inline ll score(int l, int r) {
  l = card[l]; r = card[r];
  if (l > r) { swap(l, r); }
  return (sum[r + 1] - sum[l]) / L;
}

ll calc(int left, int right) {
  if (memo[left][right] != -1) { return memo[left][right]; }
  int index = max(left, right) + 1;
  if (index == n) {
    return score(left, right);
  }
  ll ret = 1LL << 60;
  ret = min(ret, calc(index, right) + score(left, index));
  ret = min(ret, calc(left, index) + score(right, index));
  return memo[left][right] = ret;
}

int main() {
  while (scanf("%d %d %d", &n, &m, &L) > 0) {
    MEMSET(memo, -1);
    MEMSET(sum, 0);
    REP(i, n) {
      int v = scanf("%d", &card[i]);
      card[i]--;
      assert(v == 1);
    }
    REP(i, m) {
      int v = scanf("%d", &seq[i]);
      assert(v == 1);
    }
    sum[0] = 0;
    REP(i, 100000) {
      sum[i + 1] = sum[i] + seq[i];
    }
    sort(card, card + n);
    reverse(card, card + n);
    ll ans = calc(0, 0);
    printf("%lld\n", ans);
  }
}