斜率优化初涉

10 篇文章 0 订阅
2 篇文章 0 订阅

这是一个奇怪的东西- -


http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=16331

以toy为例浅谈斜率优化

(可以略过前面看分割线以后,转移方程在前面)


•直接来看这样一个问题:
•BZOJ 1010 TOY
•[HNOI2008]
•P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为1...N的N件玩具,第i件玩具经过压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一个容器中,那么容器的长度将为 x=j-i+Sigma(Ck) i<=K<=j 制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为x,其制作费用为(X-L)^2.其中L是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过L。但他希望费用最小.

Input
•第一行输入两个整数N,L.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7
Output
•输出最小费用
•相信如果是dp很快就能写出状态

•Define dp[i]为前i个玩具都装入好的最小代价
•转移:dp[i]=min{dp[j-1]+(sum[i]-sum[j-1]+i-j-L)^2}  j<=i
•前j-1个放在一起,第j到i放一起,找一个最小代价
•这样就完了?

•观察数据范围。
•N<=50000
•而上面的转移是n^2的,TLE.
•怎么办?
•我们观察之前的状态,会发现一些状态根本不是最优的,但是n^2枚举的时候仍然寻找到了,浪费了很多时间。

-------------------------------------------------------------------正文------------------------------------------------------------

•所以,引入斜率优化。
 在这里,我们得到一个转移方程.
Dp[i]=min{dp[j-1]+(sum[i]-sum[j-1]+i-j-L)^2}
定义:g[i]=sum[i]+i-L,h[j]=sum[j-1]+j
则Dp[i]=min{dp[j-1]+g[i]^2+h[j]^2-2*g[i]*h[j]}
这一步我们将i,j独立开来,得到dp[j-1],g[i]^2,h[j]^2.
但是2g[i]h[j]并没有分离


•对于刚才得到的式子:
•Dp[i]=min{dp[j-1]+g[i]^2+h[j]^2-2g[i]h[j]};
•考虑对于每一个确定的i,什么时候的j<=i使得dp[j-1]+h[j]^2-2g[i]h[j] ①最小.
•这样考虑:对于一个j1<j2<=i
•我们需要知道什么时候有dp[i](j2)<dp[i](j1)
•也就是,我们再定义y[j]=dp[j-1]+h[j]^2
•y[j2]-2g[i]h[j2]<y[j1]-2g[i]h[j1]
•y[j2]-y[j1]<2g[i](h[j2]-h[j1])    h[j]递增
•∴(y[j2]-y[j1])/(h[j2]-h[j1])<2g[i]

•观察这个式子:
•(y[j2]-y[j1])/(h[j2]-h[j1])<2g[i]
•是不是有点斜率的感觉,这个就涉及新知识斜率优化。
•我们要怎么维护.
•定义T(j1,j2)=左边<2g[i]表示满足这个关系的时候,j2比j1更优.
•也就是说T(j1,j2)>2g[i]的时候,j1比j2更优
•那么,我们就有眉目了

•已经知道T(j1,j2)>2g[i]的时候j1更优
•而且我们知道g[i]单调递增
考虑对于一组x<y,T(x,y)<2g[i]<2g[i+1]<2```
•也就是说T(x,y)在之后永远满足这个条件,也就是恒有T(x,y)<2g[i0],i0>i,y比x更优.
•x就没有必要保留.
•我们需要保留的只是T(x,y)>2g[i],这样才能保证最优解存在且没有多余的项。
•当T(j1,j2)>2g[i]的时候,j1更优
•于是我们保留2g[i]<T(j1,j2)<T(j2,j3)<``````

•2g[i]<T(j1,j2)<T(j2,j3)<```
•可以知道,j1比j2优,j2比j3优,j3比j4优·····
•最优在队首
•也就是只需要维护2g[i]<T(j1,j2)<T(j2,j3)<T(j3,j4)<T(j4,j5)<```
•最优决策取队首就行了
•用双端队列维护
>    

•这里要注意的是我们状态定义没错,但是实现起来会有一定问题(初始化下标为负(●—●))
•所以,重新定义状态转移
•Dp[i]=min{dp[j]+(sum[i]-sum[j]+i-(j+1)-L)^2}
•为了巩固之前的方法,尝试自己按照流程做一次。
•定义新函数->参数分离(i,j分开表达)->斜率表达式->讨论情况(T(x,y)?g[i])->决定最优解(T(j1,j2)?T(j2,j3))->取最优决策(怎样维护,最优解是否在队首或队尾)


☆重新分析是为了有对这个算法的整体把握,注意体会上一行算法流程的每一步的思路。


值得注意的是,这里的斜率表达式T(j1,j2)=(y[j2]-y[j1])/(h[j2]-h[j1])<2g[i]  中g[i]是单调的,并且分母表达式中函数也是单调的,   仅在这种情况下可维护单调性T(j1,j2)?T(j2,j3)?T(j3,j4),且在队首或队尾取得最优决策!!!


以上属于简单的斜率优化,重在理解分析过程,代码的实现难度并不大,一般采用双端队列维护 (建议手工队列)


下面附上AC代码:        bzoj1010

(带有简单分析)

//http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=16331
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
#define ll long long
using namespace std;
int n,L;
const int maxn=50000+20;
ll dp[maxn];
ll sum[maxn];
ll g[maxn],h[maxn];
//想法对的,但没法实现,原因:sum[j-1]为负 
//dp[i]=dp[j]+(sum[i]-sum[j]+i-(j+1)-L)^2
//g[i]=sum[i]+i-L;
//h[j]=sum[j]+j+1
//dp[i]=dp[j]+g[i]*g[i]+h[j]*h[j]-2g[i]h[j]
//对于j2>j1  dp[i](j2)<dp[i](j1) 
//dp[j2]+h[j2]^2-2g[i]h[j2]<dp[j1]+h[j1]^2-2g[i]h[j1]
//y[j]=dp[j]+h[j]^2
//y[j2]-y[j1]<2g[i](h[j2]-h[j1])
//T(x,y)=(y[j2]-y[j1])/(h[j2]-h[j1])
//维护2g[i]<T(j1,j2)<T(j2,j3)<```    最优决策在j1 
int head,tail;
int q[maxn];//第几个 
double T(int x,int y)        //得到斜率 
{
	return 1.0*(dp[x]+h[x]*h[x]-dp[y]-h[y]*h[y])/(h[x]-h[y]);
}
int main()
{
	while(scanf("%d%d",&n,&L)!=EOF)
	{
		ll x;
		head=tail=0;//队空
		
		memset(sum,0,sizeof(sum));
		memset(dp,0x3f3f3f3f,sizeof(dp));
		g[0]=-L;
		h[0]=1;
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&x);
			sum[i]=sum[i-1]+x;
			g[i]=sum[i]+i-L;
			h[i]=sum[i]+i+1;
		}		
		dp[0]=0;
		//线性预处理
		q[0]=0;
		for(int i=1;i<=n;i++)
		{
			//维护单调性2g[i]<T(j1,j2)<T(j2,j3)<T(j3,j4)
			while(head<tail&&T(q[head],q[head+1])<=2*g[i])head++;
			int t=q[head];
			dp[i]=dp[t]+g[i]*g[i]+h[t]*h[t]-2*g[i]*h[t];
			while(head<tail&&T(q[tail],i)<T(q[tail-1],q[tail]))tail--;
			q[++tail]=i;
		} 
		printf("%lld\n",dp[n]);
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值