AcWing 303 運輸小貓

題目描述:

小S是農場主,他養了 M 只貓,僱了 P 位飼養員。

農場中有一條筆直的路,路邊有 N 座山,從 1 到 N 編號。

第 i 座山與第 i-1 座山之間的距離爲 Di。

飼養員都住在 1 號山。

有一天,貓出去玩。

第 i 只貓去 Hi 號山玩,玩到時刻 Ti 停止,然後在原地等飼養員來接。

飼養員們必須回收所有的貓。

每個飼養員沿着路從 1 號山走到 N 號山,把各座山上已經在等待的貓全部接走。

飼養員在路上行走需要時間,速度爲1 米/單位時間。

飼養員在每座山上接貓的時間可以忽略,可以攜帶的貓的數量爲無窮大。

例如有兩座相距爲 1 的山,一隻貓在 2 號山玩,玩到時刻 3 開始等待。

如果飼養員從 1 號山在時刻 2 或 3 出發,那麼他可以接到貓,貓的等待時間爲 0 或 1。

而如果他於時刻 1 出發,那麼他將於時刻 2 經過 2 號山,不能接到當時仍在玩的貓。

你的任務是規劃每個飼養員從 1 號山出發的時間,使得所有貓等待時間的總和儘量小。

飼養員出發的時間可以爲負。

輸入格式

第一行包含三個整數N,M,P。

第二行包含n-1個整數,D2,D3,…,DN。

接下來M行,每行包含兩個整數 Hi和 Ti。

輸出格式

輸出一個整數,表示所有貓等待時間的總和的最小值。

數據範圍

2≤N≤10^5 ,
1≤M≤10^5,
1≤P≤100,
1≤Di<1000,
1≤Hi≤N,
0≤Ti≤10^9

輸入樣例:

4 6 2
1 3 5
1 0
2 1
4 9
1 10
2 10
3 12

輸出樣例:

3

分析:

題意比較複雜,首先要明白的是一個飼養員某時刻出發,能夠接走哪些貓,或者說,要滿足什麼條件的貓才能夠被改飼養員接走,顯然只需要到達某隻貓所在山的時候貓已經玩完了就可以接走貓了。設飼養員出發的時間是st,某隻貓在第i座山上玩,並且要玩ti時間,該飼養員能夠接走該貓只需要出發時間加上趕路時間大於等於貓玩耍的結束時間即可,即st + di >= ti時可以接走貓,di和ti都是固定的,而飼養員的出發時間是可以變化的,故st時刻出發,可以接走剩下貓中的ti - di <=st的貓。因此需要按照所有貓的玩耍結束時間減去山的距離自小到大排序,方便確定飼養員能夠接走哪些貓。

狀態表示:f[i][j]表示前i個飼養員接走排好序的前j只貓的最小等待時間。狀態轉移方程爲f[i][j] = min(f[i-1][k] + aj*(j-k)-(sj-sk)),其中aj是第j只貓的玩完時間減去山的距離,也就是第i個飼養員出發的時間,也就是說前i-1個飼養員接走了前k只貓,k + 1到j只貓由第i個飼養員接走,每隻貓的等待時間分別是aj - ak+1,aj - ak+2,...,aj - aj,求和得到aj*(j-k) - (ak+1 + ak+2 + ...+aj) = aj*(j-k)-(sj-sk),其中sj表示a1到aj的前綴和。k的取值可以從0到j-1,當然k取到j也是沒問題的,表示第i個人一隻貓都沒接到,但是顯然多一個人去接口的可以減少貓的等待時間,所以f[i-1][j]不可能是f[i][j]的最優解,或者說最多與最優解相等,因此也就不用考慮k = j的情況了。設k是使得f[i][j]取得最小值的那個k,則f[i][j] = f[i-1][k] + aj*(j-k)-(sj-sk),求f[i][j]時i和j相關的變量都是已知的,變形得到f[i-1][k] + sk = aj * k + f[i][j] - a[j]*j + sj,其中aj,j,sj都是已知的,將f[i-1][k] + sk看作y,k看作x,就得到了一個一次函數,要使f[i][j]最小即使得截距最小,就可以使用斜率優化了,具體斜率優化的推導可以參考AcWing 301 任務安排2,寫習慣後就可以直接分析這個式子的性質,隨着j的增加,新加入點集中的點(k,f[i-1][k] + s[k])也是遞增的,並且斜率aj也是遞增的,因爲之前已經對a數組排序了。所以後面的就可以完全仿照任務安排那題了,維護一個斜率自隊頭向隊尾遞增的單調隊列。

遍歷到j時,查詢使f[i][j]取得最小值的k,查詢到比當前斜率aj小的斜率都出隊,查詢到第一個大於sj的斜率就找到了k。

出隊尾時,也是要保證新加入隊尾的斜率要嚴格的大於之前隊尾的斜率,否則就要出隊尾。

至於如何求隊頭隊尾的斜率,就相當簡單了,(k,f[i-1][k] + s[k])是點座標的形式,隊頭的斜率等於k取q[hh]以及q[hh+1]這兩點構成的斜率,隊尾的斜率等於k取q[tt]和q[tt-1]這兩點構成的斜率,(斜率表達式較長,這裏就不寫了,具體表達式見代碼即可)。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100005;
typedef long long ll;
int n,m,p,q[N];
ll d[N],t[N],a[N],s[N],f[105][N];
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 i = 0;i <= p;i++)   f[i][0] = 0;
    for(int i = 1;i <= p;i++){
        int hh = 0,tt = 0;
        for(int j = 1;j <= m;j++){
            while(hh < tt && (f[i-1][q[hh+1]]+s[q[hh+1]]-f[i-1][q[hh]]-s[q[hh]])<=a[j]*(q[hh+1]-q[hh])) hh++;
            f[i][j] = f[i-1][q[hh]] + a[j] * (j - q[hh]) - (s[j] - s[q[hh]]);
            while(hh < tt && (f[i-1][j]+s[j]-f[i-1][q[tt]]-s[q[tt]])*(q[tt]-q[tt-1])<=(f[i-1][q[tt]]+s[q[tt]]-f[i-1][q[tt-1]]-s[q[tt-1]])*(j-q[tt])) tt--;
            q[++tt] = j;
        }
    }
    printf("%lld\n",f[p][m]);
    return 0;
}

 

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