斜率优化dp

算法理解

建议食用题解再结合一本通题解,一下就明白了

讲的非常清楚

本质上是我们发现现在已知 \(k\) 想要求 \(b\) 最小,其中有很多个 \((x,y)\) 可以进行计算答案,找到一个 \((x,y)\) 可以使 \(b\) 最小,博客的内容就是具体怎么做

T1,2

代码

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+5;
int n,S;
int sumc[N],sumt[N],c[N],t[N],f[N];
struct coo{
    int x,y;
}que[N];
bool comk1(coo p1,coo p2,coo p3,coo p4){
    if((p2.y-p1.y)*(p4.x-p3.x)<(p4.y-p3.y)*(p2.x-p1.x))  return 1;
    else  return 0;
}
bool comk2(coo p1,coo p2,int k){
    if((p2.y-p1.y)<k*(p2.x-p1.x))  return 1;
    else  return 0;
}
struct monqueue{
    int l,r;
    int len(){
        return r-l+1;
    }
    void init(){
        que[1]={0,0};
        l=1,r=1; 
    }  
    void add(int x,int y){
        coo k={x,y};
        if(len()<2){
            que[++r]=k;
            return;
        }
        while(len()>=2&&!comk1(que[r-1],que[r],que[r],k)){
            r--;
        }
        que[++r]=k;
    }
    int query(int k,int i){
        while(len()>=2&&comk2(que[l],que[l+1],k)){
            l++;
        }
        coo top=que[l];
        int b=top.y-top.x*k;
        return b+S*sumc[n]+sumt[i]*sumc[i];
    }
}q;
void solve(){
    q.init();
    for(int i=1;i<=n;i++){
        sumt[i]=sumt[i-1]+t[i];
        sumc[i]=sumc[i-1]+c[i];
    }
    for(int i=1;i<=n;i++){
        f[i]=q.query(S+sumt[i],i);
        q.add(sumc[i],f[i]);
    }
}
signed main(){
    scanf("%lld%lld",&n,&S);
    for(int i=1;i<=n;i++){
        scanf("%lld%lld",&t[i],&c[i]);
    }
    solve();
    printf("%lld\n",f[n]);
}

T3:

因为斜率不单调,多一个二分

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;
const int N=3e5+5;
int n,S;
int sumc[N],sumt[N],c[N],t[N],f[N];
struct coo{
    int x,y;
}que[N];
bool comk1(coo p1,coo p2,coo p3,coo p4){
    if((p2.y-p1.y)*(p4.x-p3.x)<(p4.y-p3.y)*(p2.x-p1.x))  return 1;
    else  return 0;
}
bool comk2(coo p1,coo p2,int k){
    if((p2.y-p1.y)<k*(p2.x-p1.x))  return 1;
    else  return 0;
}
struct monqueue{
    int l,r;
    int len(){
        return r-l+1;
    }
    void init(){
        que[1]={0,0};
        l=1,r=1; 
    }  
    void add(int x,int y){
        coo k={x,y};
        if(len()<2){
            que[++r]=k;
            return;
        }
        while(len()>=2&&!comk1(que[r-1],que[r],que[r],k)){
            r--;
        }
        que[++r]=k;
    }
    int dic(int k){
        if(len()==1)  return l;
        if(comk2(que[r-1],que[r],k))  return r;//特判所有斜率都小于k的情况
        int ml=l,mr=r-1;//前一个点代表斜率
        while(ml<mr){
            int mid=(ml+mr)>>1;
            if(comk2(que[mid],que[mid+1],k))  ml=mid+1;
            else mr=mid;
        }
        return ml;
    }
    int query(int k,int i){
        coo now=que[dic(k)];
        int b=now.y-now.x*k;
        return b+S*sumc[n]+sumt[i]*sumc[i];
    }
}q;
void solve(){
    q.init();
    for(int i=1;i<=n;i++){
        sumt[i]=sumt[i-1]+t[i];
        sumc[i]=sumc[i-1]+c[i];
    }
    for(int i=1;i<=n;i++){
        f[i]=q.query(S+sumt[i],i);
        q.add(sumc[i],f[i]);
    }
}
signed main(){
    scanf("%lld%lld",&n,&S);
    for(int i=1;i<=n;i++){
        scanf("%lld%lld",&t[i],&c[i]);
    }
    solve();
    printf("%lld\n",f[n]);
}

posted @ 2025-07-28 10:16  daydreamer_zcxnb  阅读(14)  评论(0)    收藏  举报