传送门
解析:
莫名其妙写了一个
的区间DP,结果一发过不了大样例。。。
然而标算的复杂度是
???才发现自己的DP考虑的太片面了(就是错了)。
思路:
首先讲一个错误思路,一个区间中间抽走一个,两边的合并起来,这样子DP就是
。
为什么是错的,很显然是错的啊。。。
万一这个区间的最优解是多抽走几个单独的区间,剩下的区间一起合并,那么这个做法就显然
了。
所以我们考虑另一种DP:
设
表示使得原区间
中剩下的数最大值为
,最小值为
所需要的最小代价,
表示将区间
中的所有数全部抽走需要的最小代价。
显然 数组的处理需要 数组, 数组的更新也需要 数组(真是蛋疼)。
考虑区间DP,设当前处理的是区间长度为 ,即所有长度小于 的区间的 都已经处理出来了
当前处理的是区间 ,那么我们需要处理出 对所有可能的 的答案, 的处理也考虑DP,并且需要用到已经处理了的 数组。
显然 ,转移的边界就是这个。
那么显然对于每个状态 ,考虑保留 位置的数,则有转移 ,其中 函数的作用就是比较两个参数,并且将第一个参数变成两个中的较小值。
然后考虑贪心转移一下所有 的 ,这个直接贪心转移,考虑将 区间中的所有数直接抽出来,剩下的必然满足原来的限制,转移就是
然后我们就处理好了所有当前需要的 , 的转移也十分明显了,将数组抽到最小值为 ,最大值为 ,然后直接一次将剩余的部分全部抽完的代价就是 。
实际上我们并不需要维护四维的 ,只需要维护三维,因为在一段状态中的 总是不变的,每次DP前初始化一下就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline int getint(){
re int num;
re char c;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return num;
}
inline void ckmin(int &a,cs int &b){
a<=b?0:(a=b);
}
cs int N=55,INF=0x3f3f3f3f;
int g[N][N][N],f[N][N],a,b;
int w[N],v[N],len;
int n;
signed main(){
n=getint();
a=getint();
b=getint();
for(int re i=1;i<=n;++i)v[i]=w[i]=getint();
sort(w+1,w+n+1);
len=unique(w+1,w+n+1)-w-1;
for(int re i=1;i<=n;++i)v[i]=lower_bound(w+1,w+len+1,v[i])-w;
memset(f,0x3f,sizeof f);
for(int re i=1;i<=n;++i)f[i][i]=a;
for(int re L=2;L<=n;++L){
for(int re l=1,r=L;r<=n;++l,++r){
for(int re i=l;i<=r;++i)
for(int re mn=1;mn<=len;++mn)
for(int re mx=mn;mx<=len;++mx)
g[i][mn][mx]=INF;
g[l][v[l]][v[l]]=0;
for(int re i=l;i<r;++i){
for(int re mn=len;mn;--mn)
for(int re mx=len;mx>=mn;--mx){
if(g[i][mn][mx]==INF)continue;
ckmin(g[i+1][min(mn,v[i+1])][max(mx,v[i+1])],g[i][mn][mx]);
for(int re j=r;j>i;--j)ckmin(g[j][mn][mx],g[i][mn][mx]+f[i+1][j]);
}
}
for(int re mn=len;mn;--mn)
for(int re mx=len;mx>=mn;--mx)
ckmin(f[l][r],g[r][mn][mx]+a+b*(w[mx]-w[mn])*(w[mx]-w[mn]));
}
}
cout<<f[1][n];
return 0;
}