P5017 NOIP2018 普及组 摆渡车

P5017 NOIP2018 普及组 摆渡车 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

显然要把人按照到达时间排序。然后考虑 dp。

\(f(i)\) 表示前 \(i\) 个人已上车或到达目的地,所需最少等待时间。

刷表转移,从 \(f(i)\) 转移到 \(f(j)\)\(j > i\)。而且转移有个条件:我们让公交车接完前 \(i\) 个人回来的时候一次性把 \([i + 1, j]\) 这些人全部接走,也就是一趟搞定,否则会造成转移重复,而且如果运好多趟你会写吗

很显然,\(f(j)\) 等于 \(f(i)\) 加上 \([i + 1, j]\) 这些人的总等待时间。要想知道这些人的总等待时间,就得知道接 \([i +1, j]\) 的这次公交什么时候发车。

那什么时候发车呢?肯定得等到 \(t[j]\) 来了才能发车,但是如果 \(t[j]\) 来的时候,公交车接完前 \(i\) 个人还没回来怎么办?

所以,我们假设公交车接完前 \(i\) 个人回来时,是时刻 \(T'\),那么公交车的发车时间就应该是 \(T = \max(T', t[j])\)。显然不能比这个更早,也不会比这个更晚。

那公交车接完前 \(i\) 个人回来又是什么时候?是 \(t[i] + m\) 吗?并不是,因为上一次接第 \(i\) 个人的那次公交车,可能比第 \(i\) 个人晚到,也就是还得考虑第 \(i\) 个人等待了一会。

所以顺利成章更换 dp 状态:设 \(f(i, k)\) 表示前 \(i\) 个人已上车或达到目的地,第 \(i\) 个人等了 \(k\) 分钟时,所需最少等待时间。

那么公交车接完前 \(i\) 个人回来是 \(T' = t[i] + k + m\),接 \([i + 1, j]\) 这批人的公交车的发车时间就是 \(T = \max(t[i] + k + m, t[j])\),这些人人的等待时间为 \(\sum\limits_{x = i + 1} ^ j T - t[x]\)。拆成 \(T \times (j - i) - \sum t[i + 1 \cdots j]\),就变成了一个 \(\mathcal{O}(1)\) 可以计算的式子了(后面那个和,循环同时统计即可,或者直接前缀和优化)。

喜提转移方程一枚:

\[f(j, T - t[j]) \stackrel{\min}{\gets} f(i, k) + T \times (j - i) - \sum t[i + 1 \cdots j], T = \max(t[i] + k + m, t[j]) \]

其中 \(a \stackrel{\min}{\gets} b\) 意为 \(a \gets \min(a, b)\)

\(f(i, k)\)\(k\) 的范围:\([0, \min(m - 1, t[i + 1] - t[i])]\)。这是因为如果 \(t[i]\) 等车等到 \(t[i + 1]\) 一起来了,那么这次来车肯定要把 \(t[i + 1]\) 一起接走而不是只接走 \(t[i]\)(第 \(i + 1\) 个人:你最好有事)

初始状态:\(t[0] = -\infty\)\(f(0, 0) = 0\),其它 \(f\) 均为 \(+\infty\)

一个细节:本题最大等待时间上界达不到 \(nt\),而是 \(2nm\)(严格小于这个),这是因为最优方案中每个人等待时间一定小于 \(2m\)

时间复杂度 \(\mathcal{O}(n^2m)\)

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2022-11-11 00:44:12 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2022-11-11 01:22:25
 */
#include <bits/stdc++.h>
inline int read() {
    int x = 0;
    bool f = true;
    char ch = getchar();
    for (; !isdigit(ch); ch = getchar())
        if (ch == '-')
            f = false;
    for (; isdigit(ch); ch = getchar())
        x = (x << 1) + (x << 3) + ch - '0';
    return f ? x : (~(x - 1));
}
inline int max(int a, int b) {
    return a > b ? a : b;
}
inline int min(int a, int b) {
    return a < b ? a : b;
}
inline bool gmi(int &a, int b) {
    return b < a ? a = b, true : false;
}

const int maxn = 505;
const int maxm = 105;

int t[maxn], f[maxn][maxm];

int main() {
    int n = read(), m = read();
    for (int i = 1; i <= n; ++i)
        t[i] = read();
    std :: sort(t + 1, t + 1 + n);

    t[0] = -105;
    std :: memset(f, 0x3f, sizeof(f));
    f[0][0] = 0;

    for (int i = 0; i <= n; ++i) {
        for (int k = 0; k <= min(m - 1, t[i + 1] - t[i]); ++k) {
            for (int j = i + 1, tot = t[i + 1]; j <= n; ++j, tot += t[j]) {
                int T = max(t[i] + k + m, t[j]);
                gmi(f[j][T - t[j]],
                f[i][k] + T * (j - i) - tot);
            }
        }
    }

    int ans = INT_MAX;
    for (int k = 0; k < m; ++k)
        gmi(ans, f[n][k]);
    printf("%d\n", ans);
    return 0;
}
posted @ 2022-11-11 01:17  dbxxx  阅读(54)  评论(0编辑  收藏  举报