2018.10.27【LOJ2292】「THUSC 2016」成绩单(区间)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/83451109

传送门


解析:

莫名其妙写了一个 O ( n 4 ) O(n^4) 的区间DP,结果一发过不了大样例。。。
然而标算的复杂度是 O ( n 5 ) O(n^5) ???才发现自己的DP考虑的太片面了(就是错了)。

思路:

首先讲一个错误思路,一个区间中间抽走一个,两边的合并起来,这样子DP就是 O ( n 4 ) O(n^4)
为什么是错的,很显然是错的啊。。。
万一这个区间的最优解是多抽走几个单独的区间,剩下的区间一起合并,那么这个做法就显然 g g gg 了。

所以我们考虑另一种DP:
g l , r , m n , m x g_{l,r,mn,mx} 表示使得原区间 < l , r > <l,r> 中剩下的数最大值为 m x mx ,最小值为 m n mn 所需要的最小代价, f l , r f_{l,r} 表示将区间 < l , r > <l,r> 中的所有数全部抽走需要的最小代价。

显然 g g 数组的处理需要 f f 数组, f f 数组的更新也需要 g g 数组(真是蛋疼)。

考虑区间DP,设当前处理的是区间长度为 L L ,即所有长度小于 L L 的区间的 f f 都已经处理出来了

当前处理的是区间 < l , r > <l,r> ,那么我们需要处理出 g l , r , m n , m x g_{l,r,mn,mx} 对所有可能的 m n , m x mn,mx 的答案, g g 的处理也考虑DP,并且需要用到已经处理了的 f f 数组。

显然 g l , l , w l , w l = 0 g_{l,l,w_l,w_l}=0 ,转移的边界就是这个。

那么显然对于每个状态 g l , i , m n , m x g_{l,i,mn,mx} ,考虑保留 r + 1 r+1 位置的数,则有转移 c h e c k m i n ( g l , i + 1 , m i n ( m n , w i + 1 ) , m a x ( m x , w i + 1 ) , g l , i , m n , m x ) checkmin(g_{l,i+1,min(mn,w_{i+1}),max(mx,w_{i+1})},g_{l,i,mn,mx}) ,其中 c h e c k m i n checkmin 函数的作用就是比较两个参数,并且将第一个参数变成两个中的较小值。

然后考虑贪心转移一下所有 i < t r i < t \leq r g l , t , m n , m x g_{l,t,mn,mx} ,这个直接贪心转移,考虑将 < i + 1 , t > <i+1,t> 区间中的所有数直接抽出来,剩下的必然满足原来的限制,转移就是 c h e c k m i n ( g l , t , m n , m x , g l , i , m n , m x + f i + 1 , t ) checkmin(g_{l,t,mn,mx},g_{l,i,mn,mx}+f_{i+1,t})

然后我们就处理好了所有当前需要的 g g f f 的转移也十分明显了,将数组抽到最小值为 m n mn ,最大值为 m x mx ,然后直接一次将剩余的部分全部抽完的代价就是 g l , r , m n , m x + a + b ( m x m n ) 2 g_{l,r,mn,mx}+a+b*(mx-mn)^2

实际上我们并不需要维护四维的 g g ,只需要维护三维,因为在一段状态中的 l l 总是不变的,每次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;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/83451109