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

点此看题面

大致题意: 给你一个序列,让你每次从中取出连续一段数,取完后序列剩下的数将会重新合在一起。设(k)为取数的次数,(min_i,max_i)为第(i)次取的数中的最小值和最大值,求(a imes k+b imessum_{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 imes(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;//输出答案
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5336.html