斜率优化DP习题集粹——从入门到放弃

来源:互联网 发布:php旅游系统 编辑:程序博客网 时间:2024/06/17 14:55

前言

斜率优化,在某种程度上说,可以看作是一种使决策具有单调性,从而降低时间复杂度的一种手段,但好像不是很easy的样子。

啊,学渣苦,学渣累。——Friedrich Taylor

决策单调性

要讲斜率优化怎么能不讲决策单调性

决策单调性是一种性质(废话),利用这一性质我们可以以更优的时间复杂度来解题

斜率优化

那么斜率优化是干什么的呢?

可以看做是斜率优化欲图维护一个凸包

在凸包上的决策具有单调性

习题

著名习题(1):玩具装箱

只要是讲单调性好像都会把这道题

不讲解

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<ctime>#include<cmath>#include<algorithm>#include<cctype>#include<iomanip>using namespace std;inline long long read(){long long i=0,f=1;char ch;for(ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-') f=-1;for(;isdigit(ch);ch=getchar())i=(i<<3)+(i<<1)+(ch^48);return i*f;}int buf[1024];inline void write(long long x){if(!x){putchar('0');return ;}if(x<0){putchar('-');x=-x;}while(x){buf[++buf[0]]=x%10,x/=10;}while(buf[0]) putchar(buf[buf[0]--]+48);return ;}#define stan 55555long long f[stan],que[stan],length[stan],pre[stan],n,boldl;double slope(int x,int y){return (f[x]-f[y]+length[x]*length[x]-length[y]*length[y])*0.5/(length[x]-length[y]);}signed main(){n=read();boldl=read()+1;for(int i=1;i<=n;++i){length[i]=read();length[i]+=length[i-1]+1;pre[i]=length[i]-boldl;}int l=1,r=1;que[1]=0;for(int i=1;i<=n;++i){while(l<r&&slope(que[l],que[l+1])<=pre[i]) ++l;f[i]=f[que[l]]+(pre[i]-length[que[l]])*(pre[i]-length[que[l]]);while(l<r&&slope(que[r],i)<slope(que[r-1],que[r])) --r;que[++r]=i;}write(f[n]);return 0;}

著名习题(2):特别行动队

不解释

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<ctime>#include<cmath>#include<algorithm>#include<cctype>#include<iomanip>#define int long longusing namespace std;inline int read(){int i=0,f=1;char ch;for(ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-') f=-1;for(;isdigit(ch);ch=getchar())i=(i<<3)+(i<<1)+(ch^48);return i*f;}int buf[1024];inline void write(int x){if(!x){putchar('0');return ;}if(x<0){putchar('-');x=-x;}while(x){buf[++buf[0]]=x%10,x/=10;}while(buf[0]) putchar(buf[buf[0]--]+48);return ;}#define stan 1111111int n,a,b,c,x,sum[stan],f[stan],l,r,que[stan];double slope(int k,int j){return double(f[k]-f[j]+a*(sum[k]-sum[j])*(sum[k]+sum[j])-b*(sum[k]-sum[j]))/double(2*a*(sum[k]-sum[j]));}signed main(){n=read();a=read();b=read();c=read();for(int i=1;i<=n;++i){x=read();sum[i]=sum[i-1]+x;}for(int i=1;i<=n;++i){while(l<r&&slope(que[l],que[l+1])<sum[i]) ++l;int k=que[l];f[i]=f[k]+a*(sum[i]-sum[k])*(sum[i]-sum[k])+b*(sum[i]-sum[k])+c;while(l<r&&slope(que[r-1],que[r])>slope(que[r],i)) --r;que[++r]=i;}write(f[n]);return 0;}

非著名习题:序列分割(BZOJ3675)

可以发现:你最后的结果和你分割的次第并没有什么关系

所以依次枚举并没有问题

至于指定次数,可以枚举k次

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<ctime>#include<cmath>#include<algorithm>#include<cctype>#include<iomanip>#define int long longusing namespace std;inline int read(){int i=0,f=1;char ch;for(ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-') f=-1;for(;isdigit(ch);ch=getchar())i=(i<<3)+(i<<1)+(ch^48);return i*f;}int buf[1024];void write(int x){if(!x){putchar('0');return ;}if(x<0){putchar('-');x=-x;}while(x){buf[++buf[0]]=x%10,x/=10;}while(buf[0]) putchar(buf[buf[0]--]+48);return ;}#define stan 111111#define sten 222int f[stan][2],sum[stan],n,k,pos,l,r,que[stan];inline int lef(const int a,const int b,const int c){return f[a][c]-f[b][c]+sum[b]*sum[b]-sum[a]*sum[a];}inline int righ(const int a,const int b){return sum[b]-sum[a];}signed main(){n=read();k=read();for(int i=1;i<=n;++i)sum[i]=read()+sum[i-1];for(int j=1;j<=k;++j){l=0,r=0,que[l]=0;pos=j&1;for(int i=1;i<=n;++i){while(l<r&&lef(que[l],que[l+1],pos^1)<=righ(que[l],que[l+1])*sum[i]) ++l;int t=que[l];f[i][pos]=f[t][pos^1]+sum[t]*(sum[i]-sum[t]);while(l<r&&lef(que[r-1],que[r],pos^1)*righ(que[r],i)>=lef(que[r],i,pos^1)*righ(que[r-1],que[r])) --r;que[++r]=i;}}write(f[n][pos]);    return 0;}
水完了立刻逃跑


原创粉丝点击