洛谷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(n^{2})$,在进行自习观察,可以发现斜率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;
}

 

posted @ 2020-04-29 14:19  GGMU  阅读(184)  评论(0编辑  收藏  举报