斜率優化(凸包優化)DP問題acm

是腦癱啊發表於2020-10-09


#寫在前面

這類問題做的過程比較偏數學

對於狀態轉移方程需要經過一些數學上的整理

之後幾道題步步深入斜率優化問題

##任務安排1

https://www.acwing.com/problem/content/302/
在這裡插入圖片描述

----c++版

#include<iostream>
#include<algorithm>
using namespace std;
#include<cstring>
const int N=5010;
int n,s;
int sumt[N],sumc[N];
int f[N];
int q[N];

int main(){
    scanf("%d%d", &n, &s);
    for(int i=1; i<=n; i++){
        int t, c;
        scanf("%d%d", &t, &c);
        sumt[i] = sumt[i-1]+t;
        sumc[i] = sumc[i-1]+c;
    }
    
    memset(f, 0x3f, sizeof f);
    f[0] = 0;
    
    for(int i=1; i<=n; i++)
        for(int j=0; j<i; j++)
            f[i] = min(f[i], f[j]+sumt[i]*(sumc[i]-sumc[j])+s*(sumc[n]-sumc[j]));
            
    printf("%d\n", f[n]);
    
    return 0;
}

##任務安排2

https://www.acwing.com/problem/content/303/

這題僅僅是資料範圍擴大了

在這裡插入圖片描述

----c++版

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

typedef long long ll;

const int N=300010;
int n,s;
ll c[N], t[N];
ll f[N];
int q[N];

int main(){
    scanf("%d%d", &n, &s);
    for(int i=1; i<=n; i++){
        scanf("%lld%lld", &t[i], &c[i]);
        t[i]+=t[i-1];
        c[i]+=c[i-1];
    }
    
    int hh=0, tt=0;
    q[0]=0;//f[0]也是一個可選答案
    for(int i=1; i<=n; i++){
        //佇列中至少有兩個數
        while(hh < tt && (f[q[hh+1]]-f[q[hh]]) <= (t[i]+s)*(c[q[hh+1]]-c[q[hh]])) hh++;
        int j = q[hh]; //對頭,即第一個斜率大於當前斜率的點
        f[i] = f[j]-(t[i]+s)*c[j]+t[i]*c[i]+s*c[n];
        while(hh < tt && (f[q[tt]]-f[q[tt-1]])*(c[i]-c[q[tt]]) >= (f[i]-f[q[tt]])*(c[q[tt]]-c[q[tt-1]]))
            tt--;//去掉隊尾的
        q[++tt] = i;
    }
    
    printf("%lld", f[n]);
    
    return 0;
}

##任務安排3

https://www.acwing.com/problem/content/304/

與上一題唯一的區別就是 t 可能是負數了
在這裡插入圖片描述

----c++版

#include<iostream>
#include<algorithm>
using namespace std;
const int N=300010;
typedef long long ll;

int n,s;
ll t[N],c[N];
ll f[N];
int q[N];

int main(){
    scanf("%d%d", &n, &s);
    for(int i=1; i<=n; i++){
        scanf("%lld%lld", &t[i], &c[i]);
        t[i]+=t[i-1];
        c[i]+=c[i-1];
    }
    
    int hh=0, tt=0;
    q[0]=0;
    for(int i=1;i<=n;i++){
        int l=hh, r=tt;
        while(l<r){
            int mid=l+r>>1;
            if(f[q[mid+1]]-f[q[mid]]>(t[i]+s)*(c[q[mid+1]]-c[q[mid]]))r = mid;
            else l=mid+1;
        }
        
        int j = q[r];
        f[i] = f[j]-(t[i]+s)*c[j]+t[i]*c[i]+s*c[n];
        while(hh < tt && (double)(f[q[tt]]-f[q[tt-1]])*(c[i]-c[q[tt-1]]) >= (double)(f[i]-f[q[tt-1]])*(c[q[tt]]-c[q[tt-1]]))
            tt--;//去掉隊尾的
        q[++tt] = i;
    }
    
    printf("%lld", f[n]);
    
    return 0;
}

##運輸小貓

https://www.acwing.com/problem/content/305/

在這裡插入圖片描述

----c++版

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long ll;

const int N=100010, M=100010, P=110;

int n,m,p;
ll d[N], t[N], a[N], s[N];
ll f[P][M];
int q[M];

ll get_y(int k, int j){
    return f[j-1][k]+s[k];
}

int main(){
    scanf("%d%d%d", &n,&m,&p);
    for(int i=2; i<=n; i++){
        scanf("%lld", &d[i]);
        d[i]+=d[i-1];
    }
    
    for(int i=1; i<=m; i++){
        int h;
        scanf("%d%lld", &h, &t[i]);
        a[i] = t[i]-d[h];//直接過濾掉了其他的山,只剩下留小貓的
    }
    
    sort(a+1, a+m+1);
    
    for(int i=1; i<=m; i++)s[i]=s[i-1]+a[i];
    
    memset(f, 0x3f, sizeof f);
    
    for(int j=0; j<=p; j++) f[j][0] = 0;//j個飼養員處理0只小貓都是0
    
    for(int j=1; j<=p; j++){
        int hh=0, tt=0;
        q[0] = 0;
        for(int i=1;i<=m;i++){
            while(hh<tt && (get_y(q[hh+1],j)-get_y(q[hh],j))<=a[i]*(q[hh+1]-q[hh]))hh++;
            int k = q[hh];
            f[j][i] = f[j-1][k]-a[i]*k+s[k]+a[i]*i-s[i];
            while(hh<tt && (get_y(q[tt],j)-get_y(q[tt-1],j))*(i-q[tt])>=
                    (get_y(i,j)-get_y(q[tt],j))*(q[tt]-q[tt-1]))tt--;
            q[++tt]=i;
        }
    }
    
    printf("%lld", f[p][m]);
    return 0;
}

相關文章