【洛谷5336】[THUSC2016] 成绩单(区间DP)

点此看题面

大致题意: 给你一个序列,让你每次从中取出连续一段数,取完后序列剩下的数将会重新合在一起。设\(k\)为取数的次数,\(min_i,max_i\)为第\(i\)次取的数中的最小值和最大值,求\(a\times k+b\times\sum_{i=1}^k(max_i-min_i)^2\)\(a,b\)为常数)。

区间\(DP\)

一道区间\(DP\)题。

考虑设\(f_{l,r}\)表示取完区间\([l,r]\)的最小代价

显然这不太好转移。

因此我们再设\(g_{l,r,Mn,Mx}\)表示在区间\([l,r]\)中,还未取走的数中最小值为\(Mn\)、最大值为\(Mx\)时的最小代价,来辅助转移。

注意,此处要先将数给离散化,否则会\(MLE\)

先考虑\(f\)如何从\(g\)转移得到,显然可以枚举\(Mn,Mx\)得到:

\[f_{l,r}=g_{l,r,Mn,Mx}+a+b\times(Mx-Mn)^2 \]

然后再考虑\(g\)的转移。

对于\(g_{l,r,Mn,Mx}\),我们显然可以把区间\([l,r]\)分成两部分,然后有两种转移方式:

  • 把区间划分为左/右端点和其他部分,放在一起取走,得到转移:\(g_{l,r,min(Mn,w_l),max(Mx,w_l)}=g_{l+1,r,Mn,Mx}\),\(g_{l,r,min(Mn,w_r),max(Mx,w_r)}=g_{l,r-1,Mn,Mx}\)
  • 枚举一个划分点\(k\),把区间划分为\([l,k]\)\([k+1,r]\)两部分,取走其中\([l,k]\)这一部分,剩余\([k+1,r]\)这一部分,得到转移:\(g_{l,r,Mn,Mx}=f_{l,k}+g_{k+1,r,Mn,Mx}\)

有了转移方程,接下来就是基础的区间\(DP\)了。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50
#define INF 1e9
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int n,a,b,dc,w[N+5],dv[N+5],f[N+5][N+5],g[N+5][N+5][N+5][N+5];
int main()
{
	RI i,l,r,k,Mn,Mx;for(scanf("%d%d%d",&n,&a,&b),i=1;i<=n;++i) scanf("%d",w+i),dv[i]=w[i];
	#define GV(x) (lower_bound(dv+1,dv+dc+1,x)-dv)
	for(sort(dv+1,dv+n+1),dc=unique(dv+1,dv+n+1)-dv-1,i=1;i<=n;++i) w[i]=GV(w[i]);//离散化
	for(l=1;l<=n;++l) for(r=l;r<=n;++r)//初始化成INF
		for(f[l][r]=INF,Mn=1;Mn<=dc;++Mn) for(Mx=Mn;Mx<=dc;++Mx) g[l][r][Mn][Mx]=INF;
	for(i=1;i<=n;++i) f[i][i]=a,g[i][i][w[i]][w[i]]=0;//初始化边界
	for(i=2;i<=n;++i) for(l=1,r=i;r<=n;++l,++r)//先枚举区间长度,再枚举区间
	{
		for(Mn=1;Mn<=dc;++Mn) for(Mx=Mn;Mx<=dc;++Mx)//把区间划分为左/右端点和其他部分
			Gmin(g[l][r][min(Mn,w[l])][max(Mx,w[l])],g[l+1][r][Mn][Mx]),
			Gmin(g[l][r][min(Mn,w[r])][max(Mx,w[r])],g[l][r-1][Mn][Mx]);
		for(Mn=1;Mn<=dc;++Mn) for(Mx=Mn;Mx<=dc;++Mx)//枚举一个划分点把区间划分为两部分
			for(k=l;k^r;++k) Gmin(g[l][r][Mn][Mx],f[l][k]+g[k+1][r][Mn][Mx]);
		for(Mn=1;Mn<=dc;++Mn) for(Mx=Mn;Mx<=dc;++Mx)//通过g求出f
			Gmin(f[l][r],g[l][r][Mn][Mx]+a+b*(dv[Mx]-dv[Mn])*(dv[Mx]-dv[Mn]));
	}printf("%d",f[1][n]),0;//输出答案
}
posted @ 2019-12-25 20:05  TheLostWeak  阅读(...)  评论(...编辑  收藏