Grand Prix of Saratov - D. Elevator - DP

題意: 有一臺電梯,可以容納無限容量的人,初始在 0 層,每次可以移動到所載人羣中 需要到達的最高層,並最終返回 0 層。每層移動時間爲 1,忽略人進出時間。現在有 nn 名按時間順序到達 0 層電梯口的人,第 ii 個人將在 tit_i 時刻到達電梯口,想去樓層 aia_i。現在詢問最短花費多少時間使得電梯將所有人運送玩並回到 0 層?

  • 1n2×1051\le n\le 2\times 10^5
  • 1ti,ai1091\le t_i,a_i\le 10^9

題解: 考慮第 ii 個人要上到第 aia_i 層,那麼排在第 ii 個人前面的人 jj,如果他要上的樓層 aj<aia_j<a_i,顯然可以讓第 jj 個人和第 ii 個人乘坐同一趟電梯,而時間不會更長。因此把這些人合併以後的 aa 數組是單調遞減的。接着考慮動態規劃,f(i)f(i) 表示第 ii 個人時候電梯啓動,把前 ii 個人送完所花費的最小時間,那麼有 f(i)=min(max(f(j)+2×aj+1,ti+2×aj+1))f(i)=\min(\max(f(j)+2\times a_{j+1},t_i+2\times a_{j+1})),其中的 max\max 部分是討論送完 jj 以後第 j+1j+1ii 的人有沒有來。

但是這樣是 O(n2)\mathcal{O}(n^2) 的,不能接受,考慮怎麼優化。注意到 f(i)f(i) 是非遞減的,而 aia_i 是遞減的,顯然對於第二部分 ti+2×aj+1t_i+2\times a_{j+1},我們可以維護一個 jj 使得 k[1,j1]k\in[1,j-1] 均有 f(k)tif(k)\le t_i,初始讓 f(i)=ti+aj+1f(i)=t_i+a_{j+1},接着只需要討論 min(f(j)+2×aj+1)\min(f(j)+2\times a_{j+1}),這東西可以用優先隊列維護,但是注意必須滿足 f(j)>tif(j)>t_i 成立,因爲 tt 也是單調遞增,如果存在 f(j)<tif(j)<t_i,顯然對於後面任何的 k>ik>i 均有 f(k)<tkf(k)<t_k,因此直接pop它就行。

代碼: 時間複雜度是 O(nlogn)\mathcal{O}(n\log n)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
struct node {
    int t, a;
} p[maxn];
ll f[maxn];
int main() {
    int n;
    while (~scanf("%d", &n)) {
        for (int i = 1; i <= n; i++) scanf("%d%d", &p[i].t, &p[i].a);
        vector<node> v;
        for (int i = n; i >= 1; i--) {
            if (v.empty() || p[i].a > v.back().a) v.push_back(p[i]);
        }
        int m = v.size();
        reverse(v.begin(), v.end());
        priority_queue<pair<ll, ll>, vector<pair<ll, ll>>, greater<pair<ll, ll>>> q;
        int id = 0;
        for (int i = 0; i < m; i++) {
            while (id < i && f[id] <= v[i].t) id++;
            f[i] = 2LL * v[id].a + v[i].t;
            while (!q.empty()) {
                pair<ll, ll> now = q.top();
                if (now.second <= v[i].t) q.pop();
                else {
                    f[i] = min(f[i], now.first);
                    break;
                }
            }
            if (i < m - 1) q.push({f[i] + 2LL * v[i + 1].a, f[i]});
        }
        printf("%lld\n", f[m - 1]);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章