YbtOJ「动态规划」第2章 区间DP

YbtOJ 大全

【例题1】石子合并

区间 D P DP DP 模板题,设 f [ i ] [ j ] f[i][j] f[i][j] 表示将第 i i i 堆到第 j j j 堆石子合并的代价,我们枚举一个断点 k k k,那么 f [ i ] [ j ] = min ⁡ ( f [ i ] [ j ] , f [ i ] [ k ] + f [ k + 1 ] [ j ] + s u m [ j ] − s u m [ i − 1 ] ) f[i][j] = \min(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]) f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[j]sum[i1]) 这样 D P DP DP 即可。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define re register
#define drep(a,b,c) for(re int a(b) ; a>=(c) ; --a)
#define rep(a,b,c) 	for(re int a(b) ; a<=(c) ; ++a)
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int M = 210;
int n;
int f[M][M],g[M][M],a[M],sum[M];
signed main(){
	memset(g,0x3f,sizeof(g));
	n = read();
	rep(i,1,n) a[i] = read(),a[i+n] = a[i];
	rep(i,1,2*n) f[i][i] = g[i][i] = 0;
	rep(i,1,2*n) sum[i] = sum[i-1] + a[i];
	for(re int len(2) ; len<=n ; ++len){
		for(re int i(1) ; i+len-1<=2*n ; ++i){
			int j = i+len-1;
			for(re int k(i) ; k<j ; ++k){
				f[i][j] = max(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
				g[i][j] = min(g[i][j],g[i][k]+g[k+1][j]+sum[j]-sum[i-1]);
			}
		}
	}
	int ans1 = 0,ans2 = 1e9;
	rep(i,1,n) ans1 = max(ans1,f[i][i+n-1]),ans2 = min(ans2,g[i][i+n-1]);
	printf("%d\n%d\n",ans2,ans1); 
	return 0;
}

【例题2】木板涂色

跟上一道题差不多,唯一的区别就是当 i i i j j j 颜色相同时, f [ i ] [ j ] = min ⁡ ( f [ i + 1 ] [ j ] , f [ i ] [ j − 1 ] ) f[i][j] = \min(f[i+1][j],f[i][j-1]) f[i][j]=min(f[i+1][j],f[i][j1])

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define re register
#define drep(a,b,c) for(re int a(b) ; a>=(c) ; --a)
#define rep(a,b,c) 	for(re int a(b) ; a<=(c) ; ++a)
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int M = 210;
char s[M];
int f[M][M];
signed main(){
	scanf("%s",s+1);
	int n = strlen(s+1);
	memset(f,0x3f,sizeof(f));
	rep(i,1,n) f[i][i] = 1;
	rep(len,2,n){
		for(re int i(1) ; i+len-1<=n ; ++i){
			int j = i+len-1;
			if(s[i] == s[j]) f[i][j] = min(f[i+1][j],f[i][j-1]);
			for(re int k(i) ; k<j ; ++k){
				f[i][j] = min(f[i][j],f[i][k]+f[k+1][j]);
			}
		}
	}
	printf("%d\n",f[1][n]);
	return 0;
}

【例题3】消除木块

具体看这

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define re register
#define drep(a,b,c) for(re int a(b) ; a>=(c) ; --a)
#define rep(a,b,c) 	for(re int a(b) ; a<=(c) ; ++a)
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
inline void print(int x){
	if(x < 0) putchar('-'),x = -x;
	if(x >= 10) print(x / 10);
	putchar(x % 10 + '0');
}
const int M = 210;
int f[M][M][M],a[M],col[M],len[M];
int T,n,tot;
inline void init(){
	memset(f,0,sizeof(f));
	memset(col,0,sizeof(col));
	memset(len,0,sizeof(len));
	tot = 0;
}
inline int dfs(int l,int r,int k){
	if(f[l][r][k]) return f[l][r][k];
	if(l == r) return (len[r]+k)*(len[r]+k);
	f[l][r][k] = max(f[l][r][k],dfs(l,r-1,0)+(len[r]+k)*(len[r]+k));
	rep(i,l,r-1) if(col[i] == col[r]) f[l][r][k] = max(f[l][r][k],dfs(l,i,k+len[r])+dfs(i+1,r-1,0));
	return f[l][r][k];
}
signed main(){
	T = read();
	rep(cnt,1,T){
		init();
		n = read();
		rep(i,1,n) a[i] = read();
		rep(i,1,n){
			if(a[i] != a[i-1]){
				col[++tot] = a[i];
				len[tot] = 1;
			}
			else len[tot]++;
		}
		printf("Case %d: %d\n",cnt,dfs(1,tot,0));
	}
	return 0;
}

【例题4】棋盘分割

f [ x 1 ] [ y 1 ] [ x 2 ] [ y 2 ] [ k ] f[x1][y1][x2][y2][k] f[x1][y1][x2][y2][k] 表示以 x 1 , y 1 x1,y1 x1,y1 为左上角, x 2 , y 2 x2,y2 x2,y2 为右下角的矩形,分成 k k k 块棋盘的最小方差。

这道题因为要切出来的也是矩形,所以我们只能横切或纵切。

对于每一种情况,分别算出要切除的哪一个矩形更优即可。注意这道题要用二维前缀和 O ( 1 ) O(1) O(1) 查询矩形的值。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define re register
#define drep(a,b,c) for(re int a(b) ; a>=(c) ; --a)
#define rep(a,b,c) 	for(re int a(b) ; a<=(c) ; ++a)
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
inline void print(int x){
	if(x < 0) putchar('-'),x = -x;
	if(x >= 10) print(x / 10);
	putchar(x % 10 + '0');
}
const int M = 20;
double f[M][M][M][M][M],ave;
int a[M][M],sum[M][M];
int n;
inline double calc(int x1,int y1,int x2,int y2){
	double res = sum[x2][y2] - sum[x2][y1-1] - sum[x1-1][y2] + sum[x1-1][y1-1] - ave;
	return res * res / n;
}
signed main(){
	n = read();
	rep(i,1,8) rep(j,1,8) a[i][j] = read();
	rep(i,1,8) rep(j,1,8) sum[i][j] = sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+a[i][j];
	ave = sum[8][8] * 1.0 / n;
	rep(x1,1,8) rep(y1,1,8) rep(x2,1,8) rep(y2,1,8) f[x1][y1][x2][y2][1] = calc(x1,y1,x2,y2);
	rep(k,2,n) rep(x1,1,8) rep(y1,1,8) rep(x2,1,8) rep(y2,1,8){
		f[x1][y1][x2][y2][k] = 1e9;
		rep(i,x1,x2-1){
			f[x1][y1][x2][y2][k] = min(f[x1][y1][x2][y2][k],f[x1][y1][i][y2][k-1]+calc(i+1,y1,x2,y2));
			f[x1][y1][x2][y2][k] = min(f[x1][y1][x2][y2][k],f[i+1][y1][x2][y2][k-1]+calc(x1,y1,i,y2));
		}
		rep(i,y1,y2-1){
			f[x1][y1][x2][y2][k] = min(f[x1][y1][x2][y2][k],f[x1][y1][x2][i][k-1]+calc(x1,i+1,x2,y2));
			f[x1][y1][x2][y2][k] = min(f[x1][y1][x2][y2][k],f[x1][i+1][x2][y2][k-1]+calc(x1,y1,x2,i));
		}
	}
	printf("%.3lf\n",sqrt(f[1][1][8][8][n]));
	return 0;
}

1. 1. 1. 删数问题

f [ i ] [ j ] f[i][j] f[i][j] 表示删去 i i i j j j 得到的最大价值。

我们一次性删除 i i i j j j,获得的价值是 ∣ a [ i ] − a [ j ] ∣ × ( j − i + 1 ) \mid a[i]-a[j] \mid \times (j-i+1) a[i]a[j]×(ji+1)。枚举 k k k 为断点,可以先删除 i i i k k k,再删除 k + 1 k+1 k+1 j j j

于是 f [ i ] [ j ] = max ⁡ ( f [ i ] [ k ] + f [ k + 1 ] [ j ] , a b s ( a [ i ] − a [ j ] ) × ( j − i + 1 ) f[i][j] = \max(f[i][k]+f[k+1][j],abs(a[i]-a[j]) \times (j-i+1) f[i][j]=max(f[i][k]+f[k+1][j],abs(a[i]a[j])×(ji+1)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define re register
#define drep(a,b,c) for(re int a(b) ; a>=(c) ; --a)
#define rep(a,b,c) 	for(re int a(b) ; a<=(c) ; ++a)
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int M = 210;
int f[M][M],a[M],sum[M];
int n;
signed main(){
	n = read();
	rep(i,1,n) a[i] = read();
	rep(i,1,n) sum[i] = sum[i-1] + a[i];
	rep(i,1,n) f[i][i] = a[i];
	rep(len,2,n){
		for(re int i(1) ; i+len-1<=n ; ++i){
			int j = i+len-1;
			for(re int k(i) ; k<j ; ++k){
				f[i][j] = max(f[i][j],max(f[i][k]+f[k+1][j],abs(a[i]-a[j])*(j-i+1)));
			}
		}
	}
	printf("%d\n",f[1][n]);
	return 0;
}

2. 2. 2. 恐狼后卫

我们在区间 l l l r r r 中枚举 k k k 表示第 k k k 个恐狼后卫最后一个被杀。那么杀死 k k k 的代价和 l − 1 l-1 l1 r + 1 r+1 r+1 有关。

f [ i ] [ j ] = min ⁡ ( f [ i ] [ k − 1 ] + f [ k + 1 ] [ j ] + ( b [ i − 1 ] + b [ j + 1 ] + a [ k ] ) × h [ k ] ) f[i][j] = \min(f[i][k-1]+f[k+1][j]+(b[i-1]+b[j+1]+a[k]) \times h[k]) f[i][j]=min(f[i][k1]+f[k+1][j]+(b[i1]+b[j+1]+a[k])×h[k]) 其中 h [ k ] h[k] h[k] 表示杀死第 k k k 头狼需要的攻击次数。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define re register
#define drep(a,b,c) for(re int a(b) ; a>=(c) ; --a)
#define rep(a,b,c) 	for(re int a(b) ; a<=(c) ; ++a)
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int M = 500;
int n,atk;
int a[M],b[M],h[M],f[M][M];
signed main(){
	n = read(),atk = read();
	rep(i,1,n) scanf("%d%d%d",&a[i],&b[i],&h[i]),h[i]--;
	rep(i,1,n) f[i][i] = (b[i-1]+b[i+1]+a[i])*(h[i]/atk+1);
	rep(len,2,n){
		for(re int i(1) ; i+len-1<=n ; ++i){
			int j = i+len-1;
			f[i][j] = 1e9;
			for(re int k(i) ; k<=j ; ++k){
				f[i][j] = min(f[i][j],f[i][k-1]+f[k+1][j]+(b[i-1]+b[j+1]+a[k])*(h[k]/atk+1));
			}
		}
	}
	printf("%d\n",f[1][n]);
	return 0;
}

3. 3. 3. 矩阵取数

我们对于每一行分开考虑,因为行与行之间互不影响。

f [ i ] [ j ] f[i][j] f[i][j] 表示从前面删了 i i i 个,从后面删了 j j j 个能获得的最大得分。那么 f [ i ] [ j ] f[i][j] f[i][j] 只跟 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j] f [ i ] [ j − 1 ] f[i][j-1] f[i][j1] 有关。我们先将所有的初始值 f [ i ] [ 0 ] f[i][0] f[i][0] f [ 0 ] [ i ] f[0][i] f[0][i] 处理好。设 b a s e [ i ] base[i] base[i] 表示 2 i 2^i 2i

f [ i ] [ 0 ] = f [ i − 1 ] [ 0 ] + a [ i ] × b a s e [ i ] f[i][0] = f[i-1][0] + a[i] \times base[i] f[i][0]=f[i1][0]+a[i]×base[i]

f [ 0 ] [ i ] = f [ 0 ] [ i − 1 ] + a [ m − i + 1 ] × b a s e [ i ] f[0][i] = f[0][i-1] + a[m-i+1] \times base[i] f[0][i]=f[0][i1]+a[mi+1]×base[i]

f [ i ] [ j ] f[i][j] f[i][j] f [ i − 1 ] [ j ] f[i-1][j] f[i1][j] 转移,得分 f [ i − 1 ] [ j ] + a [ i ] × b a s e [ i + j ] f[i-1][j]+a[i] \times base[i+j] f[i1][j]+a[i]×base[i+j]

f [ i ] [ j − 1 ] f[i][j-1] f[i][j1] 转移,得分 f [ i ] [ j − 1 ] + a [ m − j + 1 ] × b a s e [ i + j ] f[i][j-1]+a[m-j+1] \times base[i+j] f[i][j1]+a[mj+1]×base[i+j]

那么转移式 f [ i ] [ j ] = max ⁡ ( f [ i − 1 ] [ j ] + a [ i ] × b a s e [ i + j ] , f [ i ] [ j − 1 ] + a [ m − j + 1 ] × b a s e [ i + j ] ) f[i][j] = \max(f[i-1][j]+a[i] \times base[i+j],f[i][j-1]+a[m-j+1] \times base[i+j]) f[i][j]=max(f[i1][j]+a[i]×base[i+j],f[i][j1]+a[mj+1]×base[i+j])

注意这题要写高精度,不过 __ i n t 128 int128 int128 也行。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define re register
#define int __int128
#define drep(a,b,c) for(re int a(b) ; a>=(c) ; --a)
#define rep(a,b,c) 	for(re int a(b) ; a<=(c) ; ++a)
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
inline void print(int x){
	if(x < 0) putchar('-'),x = -x;
	if(x >= 10) print(x / 10);
	putchar(x % 10 + '0');
}
const int M = 200;
int f[M][M],a[M],base[M];
int n,m,ans;
signed main(){
	n = read(),m = read();
	base[0] = 1;
	rep(i,1,n+m) base[i] = base[i-1] * 2;
	while(n--){
		rep(i,1,m) a[i] = read();
		memset(f,0,sizeof(f));
		int mx = 0;
		rep(i,1,m) f[i][0] = f[i-1][0] + a[i]*base[i];
		rep(i,1,m) f[0][i] = f[0][i-1] + a[m-i+1]*base[i];
		rep(i,1,m) rep(j,1,m){
			if(i + j > m) continue;
			f[i][j] = max(f[i-1][j]+a[i]*base[i+j],f[i][j-1]+a[m-j+1]*base[i+j]);
			mx = max(mx,f[i][j]);
		}
		ans = ans + mx;
	}
	print(ans);
	putchar('\n');
	return 0;
}

4. 4. 4. 生日欢唱

f [ i ] [ j ] f[i][j] f[i][j] 表示第 i i i 个男生和第 j j j 个女生强制配对能获得的最大喜悦值。我们多加一个水平为 0 0 0 的男生和女生,最后的答案就是 f [ n + 1 ] [ n + 1 ] f[n+1][n+1] f[n+1][n+1]

我们分别对男生和女生枚举断点 k k k。设 s a [ i ] sa[i] sa[i] 表示前 i i i 个男生水平的前缀和, s b [ i ] sb[i] sb[i] 表示前 i i i 个女生水平的前缀和。

不选前一段女生,可以认为是 i − 1 i-1 i1 号男生和 k k k 号女生配对 : : : f [ i ] [ j ] = max ⁡ ( f [ i ] [ j ] , f [ i − 1 ] [ k ] + a [ i ] × b [ j ] − ( s b [ j − 1 ] − s b [ k ] ) 2 ) f[i][j] = \max(f[i][j],f[i-1][k]+a[i] \times b[j]-(sb[j-1]-sb[k])^2) f[i][j]=max(f[i][j],f[i1][k]+a[i]×b[j](sb[j1]sb[k])2)

不选前一段男生,可以认为是 k k k 号男生和 j − 1 j-1 j1 号女生配对 : : : f [ i ] [ j ] = max ⁡ ( f [ i ] [ j ] , f [ k ] [ j − 1 ] + a [ i ] × b [ j ] − ( s a [ i − 1 ] − s a [ k ] ) 2 ) f[i][j] = \max(f[i][j],f[k][j-1]+a[i] \times b[j]-(sa[i-1]-sa[k])^2) f[i][j]=max(f[i][j],f[k][j1]+a[i]×b[j](sa[i1]sa[k])2)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define re register
#define int long long 
#define drep(a,b,c) for(re int a(b) ; a>=(c) ; --a)
#define rep(a,b,c) 	for(re int a(b) ; a<=(c) ; ++a)
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
inline void print(int x){
	if(x < 0) putchar('-'),x = -x;
	if(x >= 10) print(x / 10);
	putchar(x % 10 + '0');
}
const int M = 610;
int f[M][M],a[M],b[M],s1[M],s2[M];
int n;
signed main(){
	n = read();
	rep(i,1,n) a[i] = read();
	rep(i,1,n) b[i] = read();
	rep(i,1,n) s1[i] = s1[i-1] + a[i];
	rep(i,1,n) s2[i] = s2[i-1] + b[i];
	rep(i,1,n+1) f[i][0] = f[0][i] = -1e9;
	rep(i,1,n+1) rep(j,1,n+1){
		f[i][j] = f[i-1][j-1] + a[i]*b[j];
		rep(k,0,i-2) f[i][j] = max(f[i][j],f[k][j-1]-(s1[i-1]-s1[k])*(s1[i-1]-s1[k])+a[i]*b[j]);
		rep(k,0,j-2) f[i][j] = max(f[i][j],f[i-1][k]-(s2[j-1]-s2[k])*(s2[j-1]-s2[k])+a[i]*b[j]);
	}
	printf("%lld\n",f[n+1][n+1]);
	return 0;
}

5. 5. 5. 最小代价

很神仙的一道题,也是看了半天题解才迷迷糊糊看懂。

d p [ l ] [ r ] dp[l][r] dp[l][r] 表示将区间 l l l r r r 全部取出的代价。因为每一段的取值只和这一段中的最大值和最小值有关,我们再记录一个数组 f [ l ] [ r ] [ x ] [ y ] f[l][r][x][y] f[l][r][x][y] 表示在 l l l r r r 中经过多次选取后,除了数值在 [ x , y ] [x,y] [x,y] 之间的数全部消掉的代价。

如果要把 f [ l ] [ r ] [ x ] [ y ] f[l][r][x][y] f[l][r][x][y] 中权值为 [ x , y ] [x,y] [x,y] 的这一段全部选空,代价为 A + B × ( y − x ) 2 A + B \times (y-x)^2 A+B×(yx)2

那么最后的 d p dp dp 转移式就是 d p [ i ] [ j ] = min ⁡ { f [ i ] [ j ] [ x ] [ y ] + A + B × ( y − x ) 2 } dp[i][j] = \min \{f[i][j][x][y] + A + B \times (y-x)^2 \} dp[i][j]=min{f[i][j][x][y]+A+B×(yx)2} 其中 ( 1 ≤ x < y ≤ W m a x ) (1 \leq x < y \leq W_{max}) (1x<yWmax)

考虑如何进行转移,分三种情况,枚举断点 k k k

  • f [ l ] [ r ] [ x ] [ y ] = min ⁡ { d p [ l ] [ k ] + f [ k + 1 ] [ r ] [ x ] [ y ] } f[l][r][x][y] = \min \{ dp[l][k] + f[k+1][r][x][y] \} f[l][r][x][y]=min{dp[l][k]+f[k+1][r][x][y]} ( ( ( 左半区间全选空,右半区间剩余 [ x , y ] [x,y] [x,y] ) ) )
  • f [ l ] [ r ] [ x ] [ y ] = min ⁡ { f [ l ] [ k ] [ x ] [ y ] + d p [ k + 1 ] [ r ] } f[l][r][x][y] = \min \{ f[l][k][x][y] + dp[k+1][r] \} f[l][r][x][y]=min{f[l][k][x][y]+dp[k+1][r]} ( ( ( 右半区间全选空,左半区间剩余 [ x , y ] [x,y] [x,y] ) ) )
  • f [ l ] [ r ] [ x ] [ y ] = min ⁡ { f [ l ] [ k ] [ x ] [ y ] + f [ k + 1 ] [ r ] [ x ] [ y ] } f[l][r][x][y] = \min \{ f[l][k][x][y] + f[k+1][r][x][y] \} f[l][r][x][y]=min{f[l][k][x][y]+f[k+1][r][x][y]} ( ( ( 左右区间均剩余 [ x , y ] [x,y] [x,y] ) ) )

我们只需要对于每一个 [ l , r ] [l,r] [l,r],枚举其 x x x y y y 就能求出某一段区间的答案。

初始值 d p [ i ] [ i ] = A dp[i][i] = A dp[i][i]=A f [ i ] [ i ] [ 1 ⋅ ⋅ ⋅ W i ] [ W i ⋅ ⋅ ⋅ W m a x ] = 0 f[i][i][1···W_i][W_i···W_{max}] = 0 f[i][i][1⋅⋅⋅Wi][Wi⋅⋅⋅Wmax]=0

注意 W i ≤ 1000 W_i \leq 1000 Wi1000,需要离散化一下。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define re register
#define int long long 
#define drep(a,b,c) for(re int a(b) ; a>=(c) ; --a)
#define rep(a,b,c) 	for(re int a(b) ; a<=(c) ; ++a)
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
inline void print(int x){
	if(x < 0) putchar('-'),x = -x;
	if(x >= 10) print(x / 10);
	putchar(x % 10 + '0');
}
const int M = 60;
int n,A,B,tot;
int f[M][M][M][M],dp[M][M],a[M],lsh[M];
inline void dfs(int l,int r){
	if(l > r) return;
	if(dp[l][r] != dp[0][0]) return;
	rep(x,1,tot) rep(y,x,tot) rep(k,l,r-1){
		dfs(l,k),dfs(k+1,r);
		f[l][r][x][y] = min(f[l][r][x][y],dp[l][k]+f[k+1][r][x][y]);
		f[l][r][x][y] = min(f[l][r][x][y],f[l][k][x][y]+dp[k+1][r]);
		f[l][r][x][y] = min(f[l][r][x][y],f[l][k][x][y]+f[k+1][r][x][y]);
		dp[l][r] = min(dp[l][r],f[l][r][x][y]+A+B*(lsh[y]-lsh[x])*(lsh[y]-lsh[x]));
	}
}
signed main(){
	n = read(),A = read(),B = read();
	rep(i,1,n) a[i] = read(),lsh[i] = a[i];
	memset(dp,0x3f,sizeof(dp));
	memset(f,0x3f,sizeof(f));
	sort(lsh+1,lsh+n+1);
	tot = unique(lsh+1,lsh+n+1)-lsh-1;
	rep(i,1,n) a[i] = lower_bound(lsh+1,lsh+tot+1,a[i])-lsh;
	rep(i,1,n){
		dp[i][i] = A;
		rep(j,1,a[i]) rep(k,a[i],tot) f[i][i][j][k] = 0;
	}
	dfs(1,n);
	printf("%lld\n",dp[1][n]);
	return 0;
}

相关文章

学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习...
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面...
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生...
Can’t connect to local MySQL server through socket \'/v...
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 ...
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服...