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 \cdots j]\),就变成了一个 \(\mathcal{O}(1)\) 可以计算的式子了(后面那个和,循环同时统计即可,或者直接前缀和优化)。

喜提转移方程一枚:

\[f(j, T – t[j]) \stackrel{\min}{\gets} f(i, k) + T \times (j – i) – \sum t[i \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;
}

原文地址:http://www.cnblogs.com/crab-in-the-northeast/p/luogu-p5017.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性