AcWing 302 任務安排3

題目描述:

有 N 個任務排成一個序列在一臺機器上等待執行,它們的順序不得改變。

機器會把這 N 個任務分成若干批,每一批包含連續的若干個任務。

從時刻0開始,任務被分批加工,執行第 i 個任務所需的時間是 Ti。

另外,在每批任務開始前,機器需要 S 的啓動時間,故執行一批任務所需的時間是啓動時間 S 加上每個任務所需時間之和。

一個任務執行後,將在機器中稍作等待,直至該批任務全部執行完畢。

也就是說,同一批任務將在同一時刻完成。

每個任務的費用是它的完成時刻乘以一個費用係數 Ci。

請爲機器規劃一個分組方案,使得總費用最小。

輸入格式

第一行包含兩個整數 N 和 S。

接下來N行每行有一對整數,分別爲 Ti 和 Ci,表示第 i 個任務單獨完成所需的時間 Ti 及其費用係數 Ci。

輸出格式

輸出一個整數,表示最小總費用。

數據範圍

1≤N≤3∗10^5
0≤S,Ci≤512
−512≤Ti≤512

輸入樣例:

5 1
1 3
3 2
4 3
2 3
1 4

輸出樣例:

153

分析:

AcWing 301 任務安排2中,我們給出了任務安排問題的斜率優化的解法,因爲斜率k = st[i] + S是單調遞增的,所以隊列中小於當前k的斜率一定小於後面的k,於是我們在查找第一個大於k的斜率時將小於k的斜率都刪除了,從而保證了O(n)的時間複雜度。但是本題的Ti可能是負數,儘管時間是負數設置得有些不合理,我們只能暫且認爲它是合理的,Ti是負數時,k = st[i] + S就未必單調遞增了,因爲前綴和不一定單調遞增了,這意味着,後面的k可能小於之前的k,我們不應該在尋找第一個大於k的斜率時刪掉小於k的斜率了,因爲後面的k還可能用到,但是不刪除小於k的斜率,意味着每次尋找j都需要從堵頭找起,時間複雜度最壞可能是平方級別的,爲此我們在找第一個大於k的斜率時,只能對隊列中的斜率做二分查找,用O(nlogn)的時間複雜度去解決本題了。本題的其它代碼與上題一致,故分析過程可以參考上題,唯一修改的地方就是將原來出隊頭的代碼修改爲二分找j的代碼了。

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 300005;
typedef long long ll;
ll f[N],t[N],c[N];
int q[N];
int main(){
    int n,s;
    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;
    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]]))     l = mid + 1;
            else    r = mid;
        }
        f[i] = f[q[l]] - (t[i] + s) * c[q[l]] + c[i] * t[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\n",f[n]);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章