洛谷3195 玩具装箱(dp,斜率优化)
传送门:https://www.luogu.com.cn/problem/P3195
解题思路:
一道斜率优化入门题,代码量很少,比较符合dp题目的风格,但是式子写了满满两张纸,推了很久。
很明显是一道dp,可以轻易推出dp[i]=min(dp[j]+(prefix[i]−prefix[j]+i−j−L−1)2),(i<j),其中dp[i]表示前i个玩具的最小代价,prefix表示前缀和。
我们可以定义a[i]=prefix[i]+i,b[i]=prefix[i]+i+L+1,显然,a[i],b[i]都是定值
得到:dp[i]=min(dp[j]+(a[i]−b[j])2)
继续化简得:dp[i]=dp[j]+a[i]2−2∗a[i]∗b[j]+b[j]2
进行移项,得到dp[j]+b[j]2=2∗a[i]∗b[j]+dp[i]+a[i]2
这时,我们假设x[i]=b[i],y[i]=dp[j]+b[j]2
可以得到y[j]=2∗a[i]∗x[j]+dp[i]+a[i]2
可以看出,(x,y)为i之前的若干个已求得的定点,要使得dp[i]最小,找一个点(x,y),过(x,y),斜率为2*a[i]的直线,与y周的截距最小。因此,问题就转化为了如何在[0,i-1]中找出这一个点。
如图所示的点,不难发现最终,最终选择的点必然能形成一个下凸包。以图片中第二个点为例为例,设第1,3个点形成的斜率为k,若斜率大于k,第3个点必然优于第2个点,而斜率小于k时第1个点必然由于第2个点。因此,只需动态维护一个栈,计算前i个点形成的凸包,在从凸包中寻找答案,使用差积维护一个栈即可。但是若是所有点都在凸包上,暴力枚举点还是会达到O(n2),在进行自习观察,可以发现斜率a[i]不光是定值,还是一个单调上升序列,因此斜率是递增的,所以,我们可以维护一个单调栈,若栈首两个点形成的斜率大于2*a[i],就将首元素弹出,最后的栈首的元素就是最优的j,计算dp[i]即可。时间复杂度O(n)。
最后贴上代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; typedef pair <ll,ll> pii; #define rep(i,x,y) for(int i=x;i<y;i++) #define rept(i,x,y) for(int i=x;i<=y;i++) #define per(i,x,y) for(int i=x;i>=y;i--) #define all(x) x.begin(),x.end() #define pb push_back #define fi first #define se second #define mes(a,b) memset(a,b,sizeof a) #define mp make_pair #define dd(x) cout<<#x<<"="<<x<<" " #define de(x) cout<<#x<<"="<<x<<"\n" #define debug() cout<<"I love Miyamizu Mitsuha forever.\n" const int inf=0x3f3f3f3f; const int maxn=5e4+5; ll dp[maxn]; ll prefix[maxn]; int n;ll l; ll a(int pos) { return pos+prefix[pos]; } ll b(int pos) { return pos+prefix[pos]+l+1; } ll x(int pos) { return b(pos); } ll y(int pos) { return dp[pos]+b(pos)*b(pos); } int q[maxn]; ll chaji(ll x1,ll y1,ll x2,ll y2) { return x1*y2-x2*y1; } double xielv(int a,int b) { return ( double( y(b)-y(a) ) ) / (x(b)-x(a)); } int main() { ios::sync_with_stdio(false); cin.tie(0); cin>>n>>l; prefix[0]=0; rept(i,1,n) { cin>>prefix[i]; prefix[i]+=prefix[i-1]; } int head=0,tail=0; dp[0]=0; q[0]=0; rept(i,1,n) { while(head<tail&&chaji( x(q[head])-x(q[head+1]),y(q[head])-y(q[head+1]),1,2*a(i) )<0 ) head++; dp[i]=y(q[head])+a(i)*a(i)-2*a(i)*x(q[head]); while(head<tail&&chaji( x(i)-x(q[tail-1]),y(i)-y(q[tail-1]),x(q[tail-1])-x(q[tail]),y(q[tail-1])-y(q[tail]) )<0 ) tail--; q[++tail]=i; } cout<<dp[n]<<"\n"; return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 数据库服务器 SQL Server 版本升级公告
· 程序员常用高效实用工具推荐,办公效率提升利器!
· C#/.NET/.NET Core技术前沿周刊 | 第 23 期(2025年1.20-1.26)