斜率优化dp


使用斜率优化是有条件的,在k(i)*g(j)把k(i)作斜率,g(j)横轴,必须保证都是单调递增的,如果不是就要通过添加负号改变单调性

这里不理解就看蓝书的任务安排

直线斜率:最优解,逼近(i)

点斜率:决策点,找最优(j)

截距:代表最优解的值,尽量(fi) 

单调性要求:斜率c(i),横坐标s(j)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<cstring>
#include<map>
#include<cmath> 
#include<vector>
/*
f[i][j]前i个数取出j个的最大值
f[i][j]=max(f[j][k]+(sum[i]-sum[j])*(sum[j]-sum[k])),0<=k<j  
f[j][k]=(s[i]-s[j])*s[k]+f[i][j]-s[i]*s[j]+s[j]^2
 k=s[i]-s[j],截距只和i,j有关(固定就好了
 但是发现k不具有单调性,s[k] 也没有单调性,怎么办?
 神奇的性质!!!!
 直接给每个点排序
 保证s[k]递增
 让后就变成了一般的DP 
 j为固定的状态,因为这样DP满足前面的我要用的已经DP过了 
 第一步寻找决策点 k:
 最大值,维护上凹包
 第二部用k更新状态i:
 倒序满足可以删除队尾 
  
  
*/
using namespace std;
const long long N=5010;
long long H,L,R;
long long F[N][N],plu[N],D[N];
long long ans;
struct poi{long long sig,val;}num[N];
inline bool operator <(const poi a,const poi b) {return a.val<b.val;}
inline long long read()
{
    long long x=0,f=1; char c;
    while(!isdigit(c=getchar())) if(c=='-') f=-1;
    do x=(x<<1)+(x<<3)+(c^48); while(isdigit(c=getchar()));
    return x*f;
}
double sloup(long long j,long long a,long long b){ return 1.0*(double)(F[j][a]-F[j][b])/(plu[a]-plu[b]); }
int main()
{
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    H=read();
    memset(F,-0x3f,sizeof(F));
    for(long long i=1;i<=H;i++)
    {
        long long x=read();
        plu[i]=plu[i-1]+x;
        num[i]=(poi){i,plu[i]};
        F[i][0]=0;
    }
    F[0][0]=0;
    sort(num,num+H+1);
    for(long long j=1;j<=H;j++)//f[i][j]=f[j][k]+(s[i]-s[j])*(s[j]-s[k])
    {
        L=1,R=0;
        for(long long k=0;k<=H;k++)
        {
            if(num[k].sig>=j) continue; 
            while(L<R&&sloup(j,D[R],D[R-1])<=sloup(j,num[k].sig,D[R-1])) R--;
            D[++R]=num[k].sig;//删除时必须取到等号,斜率相同,越往后截距越大 
        //s[k]单调递增,f[j][k]不一定,正是我要维护的 
        }
        for(long long i=H;~i;i--)
        //为什么倒序?因为我要保证斜率单调递减才能删除队尾,但是正序是递增,所以倒序
        //s[i]-s[j],s[j]固定,s[i]递变 
        {
            if(num[i].sig<=j) continue;
            while(L<R&&(plu[num[i].sig]-plu[j])<=sloup(j,D[L+1],D[L])) L++;//这里斜率相等,等效点,删前面的 
            F[num[i].sig][j]=max(F[num[i].sig][j],F[j][D[L]]+(plu[num[i].sig]-plu[j])*(plu[j]-plu[D[L]]));
        }
    }
    ans=-0x3fffffff;
    for(long long i=0;i<H;i++) ans=max(ans,(long long)F[H][i]);
    printf("%lld",ans);
    return 0;
}

 

J. 玩具装箱

内存限制:512 MiB时间限制:1000 ms标准输入输出
题目类型:传统评测方式:文本比较

题目描述

原题来自:HNOI 2008

P 教授要去看奥运,但是他舍不得他的玩具,于是他决定把所有的玩具运到北京。

他使用自己的压缩器进行压缩。这个压缩器可以将任意物品变成一维,再放到一种特殊的一维容器中。P 教授有编号为  的  件玩具,玩具经过压缩后会变成一维,第  件件玩具压缩后长度为 。

为了方便整理,P 教授要求:

  • 在一个一维容器中,玩具的编号是连续的;
  • 如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物。形式地说,如果要将  号玩具到  号玩具  放到同一个容器中,则容器长度不小于 。

制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为 ,其制作费用为 ,其中  是一个常量。

P 教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过 。试求最小费用。

输入格式

第一行输入两个整数 ;
接下来  行,每行一个整数 。

输出格式

输出最小费用。

这种和区间划分有关的dp很容易列出dp方程 dp[i]=min(dp[j]+(sum[i]-sum[j]+i-j-1-L)^2)

鐜式子化简,把和ij都有关的部分提出来,i部分作为斜率,横坐标是j部分,纵坐标就是只和j有关的部分,截距就是i点的最优决策

最大值,维护上凸包,在i决策前维护k最逼近的值,决策后把i放入维护斜率单调递减

const int N=100000+10;
int n,l;
ll sum[N],dp[N];
int head,tail,q[N];
inline double slope(int i,int j)
{
return ((dp[i]+(sum[i]+i)*(sum[i]+i))-dp[j]-(sum[j]+j)*(sum[j]+j))/(sum[i]+i-sum[j]-j);
}
int main()
{
n=re(),l=re();
_f(i,1,n)
{
scanf("%lldf",&sum[i]);
sum[i]+=sum[i-1];
}
head=tail=1;
_f(i,1,n)
{
while(head<tail&&slope(q[head],q[head+1])<2*(sum[i]+i-l-1))head++;
dp[i]=dp[q[head]]+(sum[i]+i-sum[q[head]]-q[head]-1-l)*(sum[i]+i-sum[q[head]]-q[head]-1-l);
while(head<tail&&slope(i,q[tail])<slope(q[tail-1],q[tail]))--tail;
q[++tail]=i;
}
chu("%lld",dp[n]);
return 0;
}

 

posted on 2022-03-20 22:03  HZOI-曹蓉  阅读(62)  评论(0)    收藏  举报