斜率优化
斜率优化
不应该是截距优化吗 雾
其实就是个线性规划问题,附上别人比我讲的好系列
要注意的是,每一次枚举,斜率都是在变化的,所以要枚举n次,而第i次枚举必须把i点加入凸包,并维护凸包的元素(表现为队列尾部出队)
而斜率优化最最最最重要的大前提是你会写dp方程
例题:玩具装箱
代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5;
int n,L;
long long a[N],sum[N],dp[N];
int q[N*2];
long long X(int i)
{
return sum[i]+i+L+1;
}
double Y(int i)
{
return dp[i]+X(i)*X(i);
}
double slope(int qx,int zx)
{
return (Y(zx)-Y(qx))/(X(zx)-X(qx));
}
int main()
{
scanf("%d %d",&n,&L);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
sum[i]=sum[i-1]+x;
a[i]=sum[i]+i;
}
dp[0]=0;
//手动队列更好使
int head,tail;
head=1;
tail=0;
q[++tail]=dp[0];
for(int i=1;i<=n;i++)
{
// head==tail 1个元素,head<tail两个元素,找第一个斜率大于直线的点
while(head<tail&&slope(q[head],q[head+1])<=2*a[i]) head++;
dp[i]=dp[q[head]]+(a[i]-X(q[head]))*(a[i]-X(q[head]));
//判断i点加入凸包,凸包的改变(表现为删掉斜率大于等于的点)
while(head<tail&&slope(q[tail-1],q[tail])>=slope(q[tail-1],i)) tail--;
q[++tail]=i;
}
printf("%lld\n",dp[n]);
return 0;
}
例题:POJ2018 Best Cow Fences
因为距离要大于等于L,所以从点L+1开始枚举,每次维护一个下凸包,因为答案单调性,暴力从对头取出点,判断i与队头的斜率更新答案,最后每次把i-L的点加入队列,维护下凸包。
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const int N=1e5+5,inf=1e9+6;
double eps=1e-6;
int n,L;
long long sum[N];
int q[N*2];
double maxn;
long long X(int i)
{
return i;
}
double Y(int i)
{
return sum[i];
}
double slope(int qx,int zx)
{
return (Y(zx)-Y(qx))/(X(zx)-X(qx));
}
int main()
{
scanf("%d %d",&n,&L);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
sum[i]=sum[i-1]+x;
}
//手动队列更好使
int head,tail;
head=1;
tail=0;
q[++tail]=0;
double maxn=-inf;
for(int i=L;i<=n;i++)
{
while(head<tail&&slope(q[head],q[head+1])<=slope(q[head],i)+eps)
head++;
while(head<tail&&slope(q[tail-1],q[tail])>=slope(q[tail],i-L+1)+eps)
tail--;
q[++tail]=i-L+1;
double slo=slope(q[head],i);
// printf("???%d %d %lf %lf\n",q[head],i,slope(q[head],i),maxn);
if(slo>maxn+eps)
{
maxn=slo;
}
}
printf("%lld\n",(long long)(maxn*1000));
return 0;
}
$道路千万条,点赞第一条;阅读不规范,笔者两行泪$