【例题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[i−1]) 这样 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][j−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;
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]∣×(j−i+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])×(j−i+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 l−1 与 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][k−1]+f[k+1][j]+(b[i−1]+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[i−1][j] 和 f [ i ] [ j − 1 ] f[i][j-1] f[i][j−1] 有关。我们先将所有的初始值 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[i−1][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][i−1]+a[m−i+1]×base[i]。
f [ i ] [ j ] f[i][j] f[i][j] 从 f [ i − 1 ] [ j ] f[i-1][j] f[i−1][j] 转移,得分 f [ i − 1 ] [ j ] + a [ i ] × b a s e [ i + j ] f[i-1][j]+a[i] \times base[i+j] f[i−1][j]+a[i]×base[i+j]。
从 f [ i ] [ j − 1 ] f[i][j-1] f[i][j−1] 转移,得分 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][j−1]+a[m−j+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[i−1][j]+a[i]×base[i+j],f[i][j−1]+a[m−j+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 i−1 号男生和 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[i−1][k]+a[i]×b[j]−(sb[j−1]−sb[k])2)
不选前一段男生,可以认为是 k k k 号男生和 j − 1 j-1 j−1 号女生配对 : : : 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][j−1]+a[i]×b[j]−(sa[i−1]−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×(y−x)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×(y−x)2} 其中 ( 1 ≤ x < y ≤ W m a x ) (1 \leq x < y \leq W_{max}) (1≤x<y≤Wmax)。
考虑如何进行转移,分三种情况,枚举断点 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 Wi≤1000,需要离散化一下。
#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;
}
原文地址:https://www.jb51.cc/wenti/3288361.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。