码迷,mamicode.com
首页 > 其他好文 > 详细

斜率优化dp

时间:2015-05-26 21:01:05      阅读:152      评论:0      收藏:0      [点我收藏+]

标签:

对于一些具有决策单调性的dp题目,我们可以应用斜率优化将复杂度从O(n^2)降到O(n)。

 

bzoj1010 HNOI2008 玩具装箱toy

题目大意:对于一些一维长度的物品,我们可以将连续的i~j个物品放在一起,费用是(j-i+sigma lk(i<=k<=j)-L)^2,求n个物品最小费用。

思路:对于这类dp题目,我们可以通过进行一系列数学化简,找出其中的斜率,进而斜率优化。我们设sum[i]表示前i件物品的长度和,s[i]为sum[i]+i,dp[i]=dp[j]+(s[i]-s[j]-L)^2。我们取j<k,dp[j,i]-dp[k,i]>0,带入之前的式子,将L+1,将含i的移到右边,含j、k的移到另一边,就会得到((dp[k,i]+sk^2)-(dp[j,i]+sj^2))/(sk-sj)<2(si-L)①,我们设左边=w[k,j],显然当w[k,j]<右边的时候,k更优。对于j<k<i,w[i,k]<w[k,j]②时,k不可能是之后用到的最优解,所以可以舍弃。我们维护可能用到的最优解,不断的舍弃那些不好的解,就能将复杂度降至O(n)。对于这里涉及两个符号的问题,我们可以统一符号(如上两个式子)。

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxnode 50005
using namespace std;
long long dp[maxnode]={0},sum[maxnode]={0},q[maxnode]={0},l;
long long getup(int j,int k)
{
    return dp[j]+(sum[j])*(sum[j])-(dp[k]+(sum[k])*(sum[k]));
}
long long getdown(int j,int k)
{
    return (sum[j]-sum[k]);
}
long long getdp(int i,int j)
{
    return dp[j]+(sum[i]-sum[j]-l)*(sum[i]-sum[j]-l);
}
double g(int j,int k)
{
    return getup(j,k)*1.0/(2.0*getdown(j,k));
}
int main()
{
    int n,i,j,head,tail;
    scanf("%d%lld",&n,&l);
    for (i=1;i<=n;++i) 
    {
      scanf("%lld",&sum[i]);
      sum[i]+=sum[i-1];
    }
    for (i=1;i<=n;++i) sum[i]+=(long long)i;
    head=tail=1;q[tail]=0;++l;
    for (i=1;i<=n;++i)
    {
        while(head<tail&& g(q[head+1],q[head]) < 1.0*(sum[i]-l))
          ++head;
        dp[i]=getdp(i,q[head]);
        while(head<tail&& g(i,q[tail]) < g(q[tail],q[tail-1]))
          --tail;
        q[++tail]=i;
    }
    printf("%lld\n",dp[n]);
}
View Code

 

bzoj1911 APIO2010特别行动队

题目大意:n名士兵,连续的一组士兵的战斗力是他们最初战斗力和x的一个二次函数,重新编队后求最大战斗力。

思路:同上,进行化简。显然dp[j,i]=dp[j]+a(sum[i]-sum[j])^2+b(sum[i]-sum[j])+c,取j<k,dp[j,i]-dp[k,i]<0,化简后有((dpk+asumk^2)-(dpj+asumj^2))/(sumk-sumj)>(2asumi+b)。然后根据符号统一原则,就可以得到g(q[head+1],q[head])>(2asumi+b),g(i,que[tail])>g(que[tail],que[tail-1])。

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxnode 1000005
using namespace std;
long long dp[maxnode]={0},sum[maxnode]={0},q[maxnode]={0},a,b,c;
long long getdp(int i,int j)
{
    return dp[j]+a*(sum[i]-sum[j])*(sum[i]-sum[j])+b*(sum[i]-sum[j])+c;
}
long long getup(int j,int k)
{
    return dp[j]+a*sum[j]*sum[j]-(dp[k]+a*sum[k]*sum[k]);
}
long long getdown(int j,int k)
{
    return sum[j]-sum[k];
}
double g(int j,int k)
{
    return (getup(j,k)*1.0)/(getdown(j,k));
}
int main()
{
    int i,j,n,head,tail;
    scanf("%d%lld%lld%lld",&n,&a,&b,&c);
    sum[0]=0;
    for (i=1;i<=n;++i)
    {
        scanf("%lld",&sum[i]);
        sum[i]+=sum[i-1];
    }
    head=tail=1;q[tail]=0;
    for (i=1;i<=n;++i)
    {
        while(head<tail&&g(q[head+1],q[head])>(sum[i]*2.0*a+b)) ++head;
        dp[i]=getdp(i,q[head]);
        while(head<tail&&g(i,q[tail])>g(q[tail],q[tail-1])) --tail;
        q[++tail]=i;
    }
    printf("%lld\n",dp[n]);
}
View Code

 

bzoj1597 土地购买

题目大意:n块土地,购买其中i块土地的代价是max(xi)*max(yi),求买下n块土地的最小价值。

思路:首先,我们对于i、j,如果xi<xj,yi<yj,那么就可以忽略i。那么我们就可以对于x升序第一关键字y降序第二关键字排列,然后对于之前的i、j忽略不计。然后就是斜率优化dp了。dp[j,i]=dp[j]+yj+1*xi。取j<k,我们可以化出(dpk-dpj)/(yk+1-yk+1)>-xi(一定要认真一步步的化式子!!!),然后符号统一一下。

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxnode 50005
using namespace std;
struct use{
    int xi,yi;
}area[maxnode],a[maxnode];
long long q[maxnode]={0},dp[maxnode]={0};
bool visit[maxnode]={false};
int my_comp(const use x,const use y)
{
    if (x.xi<y.xi) return 1;
    if (x.xi>y.xi) return 0;
    if (x.yi>y.yi) return 1;
    return 0;
}
long long getdp(int i,int j)
{
    return dp[j]+(long long)a[j+1].yi*(long long)a[i].xi;
}
long long getup(int j,int k)
{
    return dp[j]-dp[k];
}
long long getdown(int j,int k)
{
    return (long long)a[j+1].yi-(long long)a[k+1].yi;
}
double g(int j,int k)
{
    return getup(j,k)*1.0/getdown(j,k);
}
int main()
{
    int i,j,n,head,tail,tot=0;
    scanf("%d",&n);
    for (i=1;i<=n;++i) scanf("%d%d",&area[i].xi,&area[i].yi);
    sort(area+1,area+n+1,my_comp);
    for (i=1;i<=n;++i)
    {
        j=i-1;
        while(j&&area[j].yi<=area[i].yi)
        {
            visit[j]=true;--j;
        }
    }
    for (i=1;i<=n;++i)
      if (!visit[i]) a[++tot]=area[i];
    head=tail=1;q[tail]=0;
    for (i=1;i<=tot;++i)
    {
         while(head<tail&&g(q[head+1],q[head])>=-a[i].xi) ++head;
         dp[i]=getdp(i,q[head]);
         while(head<tail&&g(i,q[tail])>=g(q[tail],q[tail-1])) --tail;
         q[++tail]=i;
    }
    printf("%lld\n",dp[tot]);
}
View Code

 

斜率优化dp

标签:

原文地址:http://www.cnblogs.com/Rivendell/p/4531567.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!