T1 成绩单 bzoj 4897
题目大意:
一个数列 每次可以取出连续的一段 剩下的部分自动拼接起来 直到把序列取空 代价为:
$A \times k + B \times \sum_{i=1}^k {(max_i-min_i)^2}$
求最小代价
思路:
区间$dp_{i,j,a,b}$表示把区间$[i,j]$取到剩的数在$[a,b]$之间的代价
转移的时候把区间向里缩使得被缩的数在$[a,b]$之间 在剩余的区间中枚举断点
从两边取完或者剩$[a,b]$的状态转移过来
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 #include<cstring> 7 #include<vector> 8 #include<queue> 9 #include<complex> 10 #include<map> 11 #define rep(i,s,t) for(register int i=(s);i<=(t);++i) 12 #define dwn(i,s,t) for(register int i=(s);i>=(t);--i) 13 #define ren for(register int i=fst[x];i;i=nxt[i]) 14 #define Fill(x,t) memset(x,t,sizeof(x)) 15 #define ll long long 16 #define Cd complex<double> 17 #define inf 2139062143 18 #define MOD 998244353 19 #define MAXN 100100 20 #define MAXM 500100 21 using namespace std; 22 inline int read() 23 { 24 int x=0,f=1;char ch=getchar(); 25 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 26 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 27 return x*f; 28 } 29 int n,A,B,dp[60][60][60][60],g[MAXN],h[MAXN]; 30 int ok(int x,int l,int r) {return x>=l&&x<=r;} 31 int add(int x,int y) {return x==inf&&y==inf?inf:x+y;} 32 void upd(int &x,int y) {x=min(x,y);} 33 int main() 34 { 35 n=read(),A=read(),B=read();rep(i,1,n) g[i]=h[i]=read();Fill(dp,127); 36 sort(h+1,h+n+1);int l,r,m=unique(h+1,h+n+1)-h-1;rep(i,1,n) g[i]=lower_bound(h+1,h+m+1,g[i])-h; 37 rep(i,0,n) rep(j,1,n-i) rep(a,1,m) rep(b,a,m) 38 { 39 l=j,r=i+j;while(ok(g[l],a,b)) l++;while(ok(g[r],a,b)) r--; 40 if(l>r) dp[j][i+j][a][b]=0;else if(l==r) dp[j][i+j][a][b]=A; 41 else 42 { 43 rep(k,l,r-1) upd(dp[j][i+j][a][b],add(min(dp[l][k][a][b],dp[l][k][0][0]),min(dp[k+1][r][0][0],dp[k+1][r][a][b]))); 44 upd(dp[j][i+j][a][b],dp[l][r][0][0]); 45 } 46 if(dp[j][i+j][a][b]!=inf) upd(dp[j][i+j][0][0],dp[j][i+j][a][b]+A+B*(h[b]-h[a])*(h[b]-h[a])); 47 } 48 printf("%d\n",dp[1][n][0][0]); 49 }