Loading

斜率优化 dp 浅谈+刷题笔记

斜率优化 dp 浅谈+刷题笔记

忽略阶段变量,我们如果有这样的 dp 递推式:

\[f_{i}=\max or \min\{f_{k}+val_{k,i} \} \]

其中如果 \(val_{k,i}\) 即与 \(i\) 有关也与 \(k\) 有关。

这种方程我们通常可以用斜率优化,把凸包维护出来,在上面二分或者单调队列维护。

有一些题目并没有写题解,但是已经写了代码,干脆就放在下面:

1 P3195 [HNOI2008]玩具装箱

链接

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define int long long
#define ull unsigned long long
#define N 50010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T>  inline void read(T &x) {
	x=0; int f=1;
	char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
	for(;isdigit(c);c=getchar()) x=x*10+c-'0';
	x*=f;
}

template<typename T>  inline void write(T x) {
	if(x < 0) x=-x,putchar('-');
	if(x > 9) write(x / 10);
	putchar(x%10+'0');
}

template<typename T>  inline void writeln(T x) {
	write(x);
	puts("");
}

int n,L,c[N],f[N],sum[N];
int q[N],l,r;

inline int squ(int k){
    return k*k;
}

inline int comp_y(int k){
    return f[k]+squ(k+sum[k]);
}

inline int comp_x(int k){
    return k+sum[k];
}

inline int comp_k_2(int k){
    return 2*(k-1-L+sum[k]);
}

inline dd comp_k_1(int i1,int i2){
    dd x1=comp_x(i1),y1=comp_y(i1);
    dd x2=comp_x(i2),y2=comp_y(i2);
    return (y1-y2)/(x1-x2);
}

signed main(){
    read(n);read(L);
    for(int i=1;i<=n;i++){
        read(c[i]);sum[i]=sum[i-1]+c[i];
    }
    
    q[++r]=0;
    for(int i=1;i<=n;i++){
        while(l<r-1&&comp_k_1(q[l+1],q[l+2])<comp_k_2(i)) l++;
        if(l<r){
            int k=q[l+1];
            f[i]=f[k]+squ(i-k-1+sum[i]-sum[k]-L);
        }
        while(l<r-1&&comp_k_1(i,q[r])<comp_k_1(q[r],q[r-1])) r--;
        q[++r]=i;
    }
    printf("%lld",f[n]);
    return 0;
}

2 P4360 [CEOI2004]锯木厂选址

链接

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define int long long
#define ull unsigned long long
#define N 10001000
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

inline int Min(int a,int b){
    return a<b?a:b;
}

template<typename T>  inline void read(T &x) {
	x=0; int f=1;
	char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
	for(;isdigit(c);c=getchar()) x=x*10+c-'0';
	x*=f;
}

template<typename T>  inline void write(T x) {
	if(x < 0) x=-x,putchar('-');
	if(x > 9) write(x / 10);
	putchar(x%10+'0');
}

template<typename T>  inline void writeln(T x) {
	write(x);
	puts("");
}

int n,w[N],f[N],x[N],sumw[N],SM[N],g[N],ans=INF;
int q[N],l,r;

inline int com_x(int k){
    return sumw[k];
}

inline int com_y(int k){
    return f[k]+SM[k];
}

inline int com_k_1(int k){
    return x[k];
}

inline dd com_k_2(int i1,int i2){
    dd x1=com_x(i1),y1=com_y(i1);
    dd x2=com_x(i2),y2=com_y(i2);
    return (y1-y2)/(x1-x2);
}

signed main(){
    read(n);
    for(int i=1;i<=n;i++){
        read(w[i]);read(x[i+1]);
        x[i+1]+=x[i];sumw[i]=sumw[i-1]+w[i];
        SM[i]=SM[i-1]+x[i]*w[i];
    }
    w[n+1]=0;sumw[n+1]=sumw[n];SM[n+1]=SM[n];n++;
    for(int i=0;i<=n;i++){
        f[i]=sumw[i]*x[i]-SM[i];
    }
    q[++r]=0;
    for(int i=1;i<=n;i++){
        while(l<r-1&&com_k_2(q[l+1],q[l+2])<com_k_1(i)) l++;
        if(l<r){
            int k=q[l+1];
            g[i]=f[k]+x[i]*(sumw[i]-sumw[k])+SM[k]+x[n]*(sumw[n]-sumw[i])-SM[n];
        }
        while(l<r-1&&com_k_2(i,q[r])<com_k_2(q[r],q[r-1])) r--;
        q[++r]=i;
        ans=Min(ans,g[i]);
    }
    printf("%lld",ans);
    return 0;
//    for(int i=1;i<=n;i++) printf("%d\n",f[i]);
}

3 P3628 [APIO2010]特别行动队

链接 题解

4 CF311B Cats Transport 斜率优化 dp

链接 题解

posted @ 2021-07-03 11:33  hyl天梦  阅读(46)  评论(0编辑  收藏  举报