斜率优化

斜率优化

直接看例题

例题:P3195

解析

转移方程很简单:

这里f[i]表示前i个物品的最优代价。ac(如题目)的前缀和。

a[i]=j=1ic[j]f[i]=min(f[j]+(a[i]a[j]+ij1L)2)

g[i][j]表示表示在j处分段,前面按最优方案,后面直到i全部放到1个盒子里的代价。所以:

f[i]=min(g[i][j])g[i][j]=f[j]+(a[i]a[j]+ij1L)2

由于a[i],a[j],i,i,L,f[j]都为已知量,换元来简化计算。

b[i]=a[i]+ic[i]=a[i]+i+1+L

现在式子就很好看了

g[i][j]=f[j]+(b[i]c[j])2g[i][j]=f[j]+b[i]2+c[j]22b[i]c[j]2b[i]c[j]+g[i][j]b[i]2=f[j]+c[j]2

还记得b[i],f[j],c[j]都是常数吗,既然都是常数,那么可以把这个式子理解成2b[i]k(斜率),g[i][j]b[i]2b(截距),经过定点(c[j],f[j]+c[j]2)的一次函数!

斜率一定,且经过定点,那么g[i][j]=+b[i]

这样我们就可以通过图像来分析性质了!

从hhz6830975的题解里来的图(侵删)

13267.png (800×373) (luogu.com.cn)

由于我们要找最小的g[i][j],所以只有下凸包> 里的点可能有贡献,令P[i]表式下凸包里,从左到右第i个点。根据凸包的性质,P[i],P[i+1]的斜率随i递增,那么真正用来转移的点(使截距最大的点)就是第一个让P[i],P[i+1]>=2b[i]的点。

总结

所有转移方程可以转化成定斜率的一次函数即具有决策单调性的DP都可以考虑使用斜率优化。

这样的转移方程大多形如:

f[i]=min(max)(f[j]+(a[i]b[j])2)

大概的推导就是把下标为i的和有ij的扔一边,其他扔对面

f[i]=f[j]+a[i]2+b[j]22a[i]b[j]2a[i]b[i]+f[i]a[i]2=b[j]2+f[j]

始终记住a[i],b[j]是可以通过下标直接计算得出的常数

实现

用单调队列的思想实现,注意不同变量的不同单调性。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MM=55000;
ll n,L,c[MM],f[MM],a[MM],q[MM],h,t;
double b(int x)
{
	return a[x]+x+L+1;
}
double xl(int x,int y)
{
	return (f[x]+b(x)*b(x)-f[y]-b(y)*b(y))/(b(x)-b(y));
}
int main()
{
	cin>>n>>L;
	for(int i=1;i<=n;i++)
		cin>>a[i],a[i]+=a[i-1];
	h=t=1;
	for(int i=1;i<=n;i++)
	{
		while(h<t&&xl(q[h],q[h+1])<2*a[i]+2*i)++h;
		f[i]=f[q[h]]+(a[i]+i-b(q[h]))*(a[i]+i-b(q[h]));	
		while(h<t&&xl(q[t-1],q[t])>xl(i,q[t-1]))--t;
		q[++t]=i;
	}
	cout<<f[n];
	return 0;
} 

再来道例题

P2900

Pix表示第i块的长Piy表示第i块地的宽,如果存在Pjx>PixPjy>Piy,那么只要在买含第j块地的一组时顺便把第i块地买了就好,所以第i块地对答案不会有贡献。

考虑所有有贡献的地,将这些地按长升序排序,那么宽一定是降序的。

分析到这可以开始DP了。

f[i]表示前买前i块地的代价转移方程十分显然:

f[i]=min(f[j]+y[j+1]x[i])

O(n2)肯定过不了,考虑斜率优化。

稍微推下式子:

y[j+1]x[i]+f[i]=f[j]

x[i]为斜率,过(y[j+1],f[j])

这次是自己画的图:

如图,还是一样的维护凸包的过程,但要注意最开始在单调栈插入一个0即图中(y[1],0)的点,代表把前i个一次性买了。

实现

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MM=50004;
struct orzzy
{
	int x,y;
}p[MM];
bool cmp(orzzy a,orzzy b)
{
	if(a.y==b.y)
		return a.x<b.x;
	else
		return a.y<b.y;
}
int tot,n;
ll f[MM],x[MM],y[MM],tmp,q[MM],h,t;
double xl(int a,int b)
{
	return (double)(f[b]-f[a])/(double)(-y[b+1]+y[a+1]);
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>p[i].x>>p[i].y;
	sort(p+1,p+n+1,cmp);
	for(int i=n;i>=1;i--)
		if(p[i].x>tmp)
			tmp=p[i].x,x[++tot]=p[i].x,y[tot]=p[i].y;
	n=tot;
	h=t=1;
	for(int i=1;i<=n;i++)
	{
		while(h<t&&xl(q[h],q[h+1])<(double)(x[i]))h++;
		f[i]=f[q[h]]+y[q[h]+1]*x[i];
		while(h<t&&xl(q[t-1],q[t])>xl(q[t-1],i))t--;
		q[++t]=i;
	}
	cout<<f[n];
	return 0;
} 
posted @   T_X蒻  阅读(88)  评论(2编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
点击右上角即可分享
微信分享提示