題意: 有一臺電梯,可以容納無限容量的人,初始在 0 層,每次可以移動到所載人羣中 需要到達的最高層,並最終返回 0 層。每層移動時間爲 1,忽略人進出時間。現在有 名按時間順序到達 0 層電梯口的人,第 個人將在 時刻到達電梯口,想去樓層 。現在詢問最短花費多少時間使得電梯將所有人運送玩並回到 0 層?
題解: 考慮第 個人要上到第 層,那麼排在第 個人前面的人 ,如果他要上的樓層 ,顯然可以讓第 個人和第 個人乘坐同一趟電梯,而時間不會更長。因此把這些人合併以後的 數組是單調遞減的。接着考慮動態規劃, 表示第 個人時候電梯啓動,把前 個人送完所花費的最小時間,那麼有 ,其中的 部分是討論送完 以後第 到 的人有沒有來。
但是這樣是 的,不能接受,考慮怎麼優化。注意到 是非遞減的,而 是遞減的,顯然對於第二部分 ,我們可以維護一個 使得 均有 ,初始讓 ,接着只需要討論 ,這東西可以用優先隊列維護,但是注意必須滿足 成立,因爲 也是單調遞增,如果存在 ,顯然對於後面任何的 均有 ,因此直接pop
它就行。
代碼: 時間複雜度是
#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;
}