1. 程式人生 > >bzoj4897「THUSC 2016」成績單(區間dp)

bzoj4897「THUSC 2016」成績單(區間dp)

題目描述
期末考試結束了,班主任 L 老師要將成績單分發到每位同學手中。L老師共有 n n 份成績單,按照編號從 1 1 n

n 的順序疊放在桌子上,其中編號為 i i 的成績單分數為 W i
W_i

成績單是按照批次發放的。發放成績單時,L 老師會從當前的一疊成績單中抽取連續的一段,讓這些同學來領取自己的成績單。當這批同學領取完畢後,L 老師再從剩餘的成績單中抽取連續的一段,供下一批同學領取。經過若干批次的領取後,成績單將被全部發放到同學手中。
然而,分發成績單是一件令人頭痛的事情,一方面要照顧同學們的心理情緒,不能讓分數相差太遠的同學在同一批領取成績單;另一方面要考慮時間成本,儘量減少領取成績單的批次數。對於一個分發成績單的方案,我們定義其代價為:
a
× k + b × i = 1 k ( max i min i ) 2 a \times k+b \times \sum_{i=1}^{k}(\text{max}_i-\text{min}_i)^2

其中 k k 是分發的批次數,對於第 i i 批分發的成績單, max i \text{max}_i 是最高分數, min i \text{min}_i
是最低分數, a a b b 是給定的評估引數。 現在,請你幫助 L 老師找到代價最小的分發成績單的方案,並將這個最小的代價告訴 L 老師。當然,分發成績單的批次數 k k 是由你決定的。
輸入格式
第一行包含一個正整數 n n ,表示成績單的數量。 第二行包含兩個非負整數 a , b a,b ,表示給定的評估引數。 第三行包含 n n 個正整數 , w i w_i 表示第 i 張成績單上的分數。
輸出格式
僅一個正整數,表示最小的代價是多少。
樣例
樣例輸入
10
3 1
7 10 9 10 6 7 10 7 1 2
樣例輸出
15
資料範圍與提示
n 50 , a 1500 , b 10 , w i 1000 n \leq 50, a \leq 1500, b \leq 10, w_i \leq 1000


s o l u t i o n solution
把題目給的限制無腦丟進 d p dp 方程就行了
f [ l ] [ r ] [ u p ] [ d o w n ] f[l][r][up][down] 表示 [ l , r ] [l,r] 這段區間全部消完,且 [ u p , d o w n ] [up,down] 這段值域不選的花費
a n s [ l ] [ r ] ans[l][r] 表示 [ l , r ] [l,r] 的答案

不難得到 a n s ans 的轉移
a n s [ l ] [ r ] = m i n k , a , b { f [ l ] [ k ] [ a ] [ b ] + a n s [ k + 1 ] [ r ] } ans[l][r]=min_{k,a,b}\{ f[l][k][a][b] +ans[k+1][r] \}
f f 的轉移可以考慮末尾新增一個元素,反過來寫就是
f [ l ] [ r ] [ m i n ( a , v [ r ] ) ] [ m a x ( a , v [ r ] ) ] = m i n k { f [ l ] [ k ] [ a ] [ b ] + g [ k + 1 ] [ r 1 ] } f[l][r][min(a,v[r])][max(a,v[r])]=min_{k}\{f[l][k][a][b]+g[k+1][r-1]\}

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x],y = e[i].y;i;i = e[i].n,y = e[i].y)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
int rd()
{
	int num = 0;char c = getchar();bool flag = true;
	while(c < '0'||c > '9') {if(c == '-') flag = false;c = getchar();}
	while(c >= '0' && c <= '9') num = num*10+c-48,c = getchar();
	if(flag) return num;else return -num;
}
inline void chmin(int &a,int b){if(a>b)a=b;}
inline int min(int a,int b){return a<b?a:b;}
inline int max(int a,int b){return a>b?a:b;}
int n,A,B;
int a[60],tmp[60],tot;
int g[60][60],f[60][60][60][60];
int main()
{
	n = rd();A = rd();B = rd();
	rep(i,1,n) a[i] = tmp[i] = rd();tot = n;
	sort(tmp+1,tmp+tot+1);
	tot = unique(tmp+1,tmp+tot+1) - tmp - 1;
	rep(i,1,n) a[i] = lower_bound(tmp+1,tmp+tot+1,a[i]) - tmp;
	memset(f,10,sizeof(f));
	memset(g,10,sizeof(g));
	rep(i,1,n)
	{
		f[i][i][a[i]][a[i]] = 0;
		g[i][i] = A;
		g[i][i-1] = 0;
	}
	g[n+1][n] = 0;
	rep(len,2,n)
		rep(l,1,n-len+1)
		{
			int r = l+len-1,v = a[r]; 
			rep(k,l,r-1) rep(a,1,tot) rep(b,a,tot)
			{
				int ta = min(a,v),tb = max(b,v);
				chmin(f[l][r][ta][tb],f[l][k][a][b]+g[k+1][r-1]);
			}
			rep(k,l,r)
			    rep(a,1,tot) rep(b,a,tot)
			        chmin(g[l][r],f[l][k][a][b]+g[k+1][r]+A+B*(tmp[b]-tmp[a])*(tmp[b]-tmp[a]));
		}
	printf("%d\n",g[1][n]);
	return 0;
}