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

传送门


解析:

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

思路:

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

所以我们考虑另一种DP:
gl,r,mn,mxg_{l,r,mn,mx}gl,r,mn,mx表示使得原区间&lt;l,r&gt;&lt;l,r&gt;<l,r>中剩下的数最大值为mxmxmx,最小值为mnmnmn所需要的最小代价,fl,rf_{l,r}fl,r表示将区间&lt;l,r&gt;&lt;l,r&gt;<l,r>中的所有数全部抽走需要的最小代价。

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

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

当前处理的是区间&lt;l,r&gt;&lt;l,r&gt;<l,r>,那么我们需要处理出gl,r,mn,mxg_{l,r,mn,mx}gl,r,mn,mx对所有可能的mn,mxmn,mxmn,mx的答案,ggg的处理也考虑DP,并且需要用到已经处理了的fff数组。

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

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

然后考虑贪心转移一下所有i&lt;t≤ri &lt; t \leq ri<trgl,t,mn,mxg_{l,t,mn,mx}gl,t,mn,mx,这个直接贪心转移,考虑将&lt;i+1,t&gt;&lt;i+1,t&gt;<i+1,t>区间中的所有数直接抽出来,剩下的必然满足原来的限制,转移就是checkmin(gl,t,mn,mx,gl,i,mn,mx+fi+1,t)checkmin(g_{l,t,mn,mx},g_{l,i,mn,mx}+f_{i+1,t})checkmin(gl,t,mn,mx,gl,i,mn,mx+fi+1,t)

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

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

转载于:https://www.cnblogs.com/zxyoi/p/10047152.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值