BZOJ4897: [Thu Summer Camp2016]成績單【DP of DP】

Description

期末考試結束了,班主任L老師要將成績單分發到每位同窗手中。L老師共有n份成績單,按照編號從1到n的順序疊c++

放在桌子上,其中編號爲i的成績單分數爲w_i。成績單是按照批次發放的。發放成績單時,L老師會從當前的一疊spa

成績單中抽取連續的一段,讓這些同窗來領取本身的成績單。當這批同窗領取完畢後,L老師再從剩餘的成績單中code

抽取連續的一段,供下一批同窗領取。通過若干批次的領取後,成績單將被所有發放到同窗手中。然而,分發成績blog

單是一件使人頭痛的事情,一方面要照顧同窗們的心理情緒,不能讓分數相差太遠的同窗在同一批領取成績單;另遞歸

一方面要考慮時間成本,儘可能減小領取成績單的批次數。對於一個分發成績單的方案,咱們定義其代價爲:ip

img

其中,k是方案中分發成績單的批次數,對於第i批分發的成績單,〖max〗_i是最高分數,〖min〗_i是最低分數。get

a,b是給定的評估參數。如今,請你幫助L老師找到代價最小的分發成績單的方案,並將這個最小的代價告訴L老師input

。固然,分發成績單的批次數k是由你決定的。it

Input

第一行包含一個正整數n,表示成績單的數量。io

第二行包含兩個非負整數a,b,表示給定的評估參數。

第三行包含n個正整數w_i,表示第i張成績單上的分數。

Output

僅一個正整數,表示最小的代價是多少。

Sample Input

10
3 1
7 10 9 10 6 7 10 7 1 2

Sample Output

15
【樣例數聽說明】
第1批:第2至4份成績單,落差值爲1,剩餘成績單爲76710712;
第2批:第4份成績單,落差值爲0,剩餘成績單爲767712;
第3批:第1至4份成績單,落差值爲1,剩餘成績單爲12;
第4批:剩餘的2份成績單,落差值爲1。
總代價爲4×3+(1^2+0^2+1^2+1^2)×1=15。

HINT

n<=50, a<=100, b<=10, w_i<=1000


思路

首先考慮dp

\(f_{l,r}\)表示把區間\([l,r]\)的全部成績單刪除的代價

而後就不會了,考慮整個區間,要麼遞歸成兩個子區間分別處理,或者直接考慮這個區間的最後一次刪除

那麼最後一次刪除的若是是\([p,q]\)這個區間的數,記錄\(g_{l,r,p,q}\)表示區間\([l,r]\)最後只剩下\([p,q]\)的最小代價

而後f的轉移能夠枚舉\(p,q\)從g轉移也能夠劃分紅兩個區間遞歸成f轉移

g能夠劃分紅兩個區間,分別從\(g_{l,k,p,q}+g_{k+1,r,p,q},g_{l,k,p,q}+f_{k+1,r},f_{l,k}+g_{k+1,r,p,q}\)轉移


#include<bits/stdc++.h>

using namespace std;

const int N = 51;
const int INF_of_int = 1e8;

int f[N][N], g[N][N][N][N];
int a, b, n, m, w[N], pre[N];

int getg(int l, int r, int down, int up);
int getf(int l, int r);

int getg(int l, int r, int down, int up) {
  if (~g[l][r][down][up]) return g[l][r][down][up];
  if (l == r) return g[l][r][down][up] = (down <= w[l] && w[l] <= up) ? 0 : INF_of_int;
  int &cur = g[l][r][down][up];
  cur = INF_of_int;
  for (int k = l; k < r; k++) {
    cur = min(cur, getg(l, k, down, up) + getg(k + 1, r, down, up));
    cur = min(cur, getg(l, k, down, up) + getf(k + 1, r));
    cur = min(cur, getf(l, k) + getg(k + 1, r, down, up));
  }
  return cur;
}

int getf(int l, int r) {
  if (~f[l][r]) return f[l][r];
  if (l == r) return f[l][r] = a;
  int &cur = f[l][r];
  cur = INF_of_int;
  for (int i = 1; i <= m; i++)
    for (int j = i; j <= m; j++)
      cur = min(cur, getg(l, r, i, j) + a + b * (pre[j] - pre[i]) * (pre[j] - pre[i]));
  for (int k = l; k < r; k++)
    cur = min(cur, getf(l, k) + getf(k + 1, r));
  return cur;
}

int main() {
#ifdef dream_maker
  freopen("input.txt", "r", stdin);
#endif
  memset(f, -1, sizeof(f));
  memset(g, -1, sizeof(g));
  scanf("%d %d %d", &n, &a, &b);
  for (int i = 1; i <= n; i++) {
    scanf("%d", &w[i]);
    pre[i] = w[i];
  }
  sort(pre + 1, pre + n + 1);
  m = unique(pre + 1, pre + n + 1) - pre - 1;
  for (int i = 1; i <= n; i++)
    w[i] = lower_bound(pre + 1, pre + m + 1, w[i]) - pre;
  printf("%d", getf(1, n));
  return 0;
}
相關文章
相關標籤/搜索