loj 2292「THUSC 2016」成绩单

loj

看着就很区间dp,所以考虑求(f_{i,j})表示区间([i,j])的答案.注意到贡献答案的方式是每次选一个连续段,拿走后剩下的段拼起来继续段,所以转移就考虑从最后一次选的方法转移过来,那么最后一次选的是原序列的一个连续段中挖掉一些小连续段的一些段.设辅助状态(g_{i,j,p,q})表示区间([i,j])要选出一个连续段,其中最小值为(p),最大值为(q)的最小代价,转移可以在左右两边接上一个(f_{i,j})(这一段不在最终段中),或者接上一个在段内的元素,并更新最大值/最小值.最后用(g)更新(f),大概为(f_{i,j}=min(a+g_{i,j,p,q}+b(q-p)^2))

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=55,mod=1e9+7;
int rd()
{
	int x=0,w=1;char ch=0;
	while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*w;
}
int n,a,b,t,w[N],bb[N],f[N][N],g[N][N][N][N];

int main()
{
	n=rd(),a=rd(),b=rd();
	for(int i=1;i<=n;++i) w[i]=bb[i]=rd();
	sort(bb+1,bb+n+1),t=unique(bb+1,bb+n+1)-bb-1;
	for(int i=1;i<=n;++i) w[i]=lower_bound(bb+1,bb+t+1,w[i])-bb;
	memset(f,0x3f3f3f,sizeof(f)),memset(g,0x3f3f3f,sizeof(g));
	for(int i=1;i<=n;++i) f[i][i]=a,g[i][i][w[i]][w[i]]=0;
	for(int i=1;i<=n;++i)
	{
		g[i-1][i][w[i]][w[i]]=min(g[i-1][i][w[i]][w[i]],g[i][i][w[i]][w[i]]+f[i-1][i-1]);
		g[i][i+1][w[i]][w[i]]=min(g[i][i+1][w[i]][w[i]],g[i][i][w[i]][w[i]]+f[i+1][i+1]);
		int np=min(w[i],w[i-1]),nq=max(w[i],w[i-1]);
		g[i-1][i][np][nq]=min(g[i-1][i][np][nq],g[i][i][w[i]][w[i]]);
		np=min(w[i],w[i+1]),nq=max(w[i],w[i+1]);
		g[i][i+1][np][nq]=min(g[i][i+1][np][nq],g[i][i][w[i]][w[i]]);
	}
	for(int l=1;l<n;++l)
		for(int i=1,j=l+1;j<=n;++i,++j)
		{
			for(int p=1;p<=t;++p)
				for(int q=p;q<=t;++q)
				{
					for(int k=i;k<j;++k)
						g[i][j][p][q]=min(g[i][j][p][q],min(g[i][k][p][q]+f[k+1][j],g[k+1][j][p][q]+f[i][k]));
					int np=min(p,w[i-1]),nq=max(q,w[i-1]);
					g[i-1][j][np][nq]=min(g[i-1][j][np][nq],g[i][j][p][q]);
					np=min(p,w[j+1]),nq=max(q,w[j+1]);
					g[i][j+1][np][nq]=min(g[i][j+1][np][nq],g[i][j][p][q]);
					f[i][j]=min(f[i][j],a+g[i][j][p][q]+b*(bb[q]-bb[p])*(bb[q]-bb[p]));
				}
		}
	printf("%d
",f[1][n]);
	return 0;
}
原文地址:https://www.cnblogs.com/smyjr/p/11986403.html