题意:http://www.lydsy.com/JudgeOnline/problem.php?id=4897
首先每一次取的代价是相互独立的,其次考虑对于值域相同的一段,拿走它的代价是一定的,所以凡是值域相同的一段都可以同等看待。那么可以设f[i][j][k][l]表示i到j,剩下的数值域是k到l的最小代价,注意不一定要把所有k到l内的数都剩下。转移时对于一个区间,枚举一个要扩张到的端点,把中间隔着的数以最优方案全部取完即可。对于每个状态,把这些剩下的数一次性取完的代价是已知的,以此来更新把这个区间全部取完的最优值。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define gm 52
using namespace std;
int n,a,b,w[gm],d[gm];
int f[gm][gm],g[gm][gm][gm][gm];
inline void cmin(int &a,int b){b<a?a=b:0;}
int main()
{
memset(f,0x3c,sizeof f);memset(g,0x3c,sizeof g);
scanf("%d%d%d",&n,&a,&b);
for(int i=1;i<=n;++i){scanf("%d",w+i);d[i]=w[i];}
sort(d+1,d+n+1); int tot=unique(d+1,d+n+1)-d-1;
for(int i=1;i<=n;++i) {w[i]=lower_bound(d+1,d+tot+1,w[i])-d;f[i][i]=a;g[i][i][w[i]][w[i]]=f[i][i-1]=0;memset(g[i][i-1],0,sizeof g[i][i-1]);}
f[n+1][n]=0;
for(int L=2;L<=n;++L)
for(int l=1,r=L;r<=n;++l,++r)
{
for(int m=l-1;m<r;++m)
for(int i=1;i<=tot;++i)
for(int j=i;j<=tot;++j)
cmin(g[l][r][min(i,w[r])][max(j,w[r])],g[l][m][i][j]+f[m+1][r-1]);
for(int m=l;m<=r;++m)
for(int i=1;i<=tot;++i)
for(int j=i;j<=tot;++j)
cmin(f[l][r],g[l][m][i][j]+a+b*(d[j]-d[i])*(d[j]-d[i])+f[m+1][r]);
}
printf("%d\n",f[1][n]);
return 0;
}