前言

在考场被这个题搞自闭了,那个时候自己是真的太菜了。qwq
现在水平稍微高了一点,就过来切一下这一道\(DP\)经典好题。
附加一个题目链接:【洛谷】

正文

虽然题目非常的简短,但是解法有很多。
我按照时间复杂度来写一下一些做法。
博主只考虑了一些基于时间的做法,其他的再补。。

时间复杂度:\(O(t^2n)\)

借鉴sooke大佬的想法,把问题抽象成一个数轴。
每一个人上车的时间就是在数轴上可能重合的一个点,一辆公交车抽象成在数轴上的一条长度为m的线段进行一次覆盖。
因为考虑到上下车时间忽略不计,那么就把这个线段看成一个左开右闭的线段。
那么问题就变成了,所有人到覆盖这个点的线段右端点的距离之和,我们的任务就是让这个和最小。
如果文字看不懂,那么就看下图:

上图中的绿色方块部分就是线段覆盖的区间。
因为我们将公交车抽象成了一个左开右闭的线段,下一辆车最早可以出发的时间
至于为什么要左开右闭,是因为可以正向的做\(DP\)。
转化出来了一个非常经典的模型。
\(f_i\)表示最后一段的右端点是\(i\),对于每一个\(i\)我们需要找到转移到\(f_i\)的决策。
枚举\(j\)也就是前一段。
可以得到一个基础的\(DP\)方程:\(f_i=min(f_j+\sum^{}_{j<tk\leq i}i-t_k)\)
其中的\(t_k\)是每一个人到的时间。也就是数轴上的各个点。
什么优化都没有的\(DP\),枚举\(i,j,k\)。
期望得分:30分

时间复杂度:\(O(t^2)\)

考虑优化上述\(DP\)。
先把式子搬下来
\[f_i=min(f_j+\sum^{}_{j<tk\leq i}i-t_k)\]
由\(\sum\)可以发现可以用前缀和优化。
那么我们就试着把这个\(\sum\)拆成前缀和的形式。
\[\sum^{}_{j<tk\leq i}i-t_k=\sum^{}_{j<tk\leq i}i-\sum^{}_{j<tk\leq i}t_k=(psum_i-psum_j)\times i-(tsum_i-tsum_j)\]
其中的\(psum\)表示的是在区间内符合的个数,\(tsum\)表示的是在区间内符合的时间的总和。

#include <bits/stdc++.h>
using namespace std;
namespace IOstream {
    #define gc getchar
    template <typename T>
    inline void read(T &x) {
        x = 0; T fl = 1; char c = 0;
        for (; c < '0' || c > '9'; c = gc()) if (c == '-') fl = -1;
        for (; c >= '0' && c <= '9'; c = gc()) x = (x << 1) + (x << 3) + (c ^ 48);
        x *= fl;
    }
    #undef gc
} using namespace IOstream;
const int N = 4e6 + 506;
const int inf = 0x3f3f3f3f;
int psum[N], tsum[N], f[N];
int n, m, t;
int main() {
    read(n); read(m);
    if (m == 1) { puts("0"); return 0; }
    for (int i = 1, x; i <= n; i ++) {
        read(x); t = max(x, t);
        psum[x] ++; tsum[x] += x;
    }
    for (int i = 0; i < t + m; i ++) {
        tsum[i] += tsum[i - 1];
        psum[i] += psum[i - 1];
    }
    memset(f, inf, sizeof(f));
    for (int i = 0; i < t + m; i ++) {
        f[i] = psum[i] * i - tsum[i];
        for (int j = 0; j + m <= i; j ++) {
            f[i] = min(f[i], f[j] + (psum[i] - psum[j]) * i - (tsum[i] - tsum[j]));
        }
    }
    int ans = inf;
    for (int i = t; i < t + m; i ++) ans = min(ans, f[i]);
    cout << ans << endl;
    return 0;
}

时间复杂度:\(O(t)\)

再是这个式子
\[f_i=min(f_j+\sum^{}_{j<tk\leq i}i-t_k)\]
可以发现这个东西和斜率优化的基本套路是一样的。
那么稍微推导一下
将前缀和的式子拿出来\(f_i=f_j+(psum_i-psum_j)\times i-(tsum_i-tsum_j)\)
把和\(i\)有关的项都放到一边,把其他的\(j\)和\(k\)的项放到另外一边。
最终可以化简为
\[\underline{f_j+tsum_j}_y=\underline{i}_k\times \underline{psum_i}_x+\underline{(f_i+psum_j\times i-tsum_i)}_b\]

对应下面这个东西
\[y=kx+b\]

开始斜率优化。
可以发现斜率\(i\)递增,然后维护下凸包。

#include <bits/stdc++.h>
using namespace std;
namespace IOstream {
    #define gc getchar
    template <typename T>
    inline void read(T &x) {
        x = 0; T fl = 1; char c = 0;
        for (; c < '0' || c > '9'; c = gc()) if (c == '-') fl = -1;
        for (; c >= '0' && c <= '9'; c = gc()) x = (x << 1) + (x << 3) + (c ^ 48);
        x *= fl;
    }
    #undef gc
} using namespace IOstream;
typedef double db;
const int N = 4e6 + 506;
const int inf = 0x3f3f3f3f;
int psum[N], tsum[N], f[N], q[N << 1];
// psum记录的是人数前缀和,tsum表示总时间的前缀和
int n, m, T;
db Y(int i) { return 1.0 * (- f[i] - tsum[i]); }
db X(int i) { return 1.0 * (- psum[i]); }
db slope(int i, int j) { return (Y(i) - Y(j)) / (psum[i] == psum[j] ? 1e-9 : (X(i) - X(j))); }
int main() {
    read(n); read(m);
    if (m == 1) { puts("0"); return 0; }
    for (int i = 1, x; i <= n; i ++) {
        read(x); T = max(x, T);
        psum[x] ++; tsum[x] += x;
    }
    for (int i = 0; i < T + m; i ++) {
        tsum[i] += tsum[i - 1];
        psum[i] += psum[i - 1];
    }
    int h = 1, t = 0;
    for (int i = 0; i < T + m; i ++) {
        if (i >= m) {
            while (h < t && slope(q[t - 1], q[t]) >= slope(q[t], i - m)) t --;
            q[++ t] = i - m;
        }
        while (h < t && slope(q[h], q[h + 1]) <= i) h ++;
        f[i] = psum[i] * i - tsum[i];
        int j = q[h];
        if (h <= t) f[i] = min(f[i], f[j] + (psum[i] - psum[j]) * i - (tsum[i] - tsum[j]));
    }
    int ans = inf;
    for (int i = T; i < T + m; i ++) ans = min(ans, f[i]);
    cout << ans << endl;
    return 0;
}

「洛谷5017」「NOIP2018」摆渡车【DP,经典好题】的更多相关文章

  1. 「区间DP」「洛谷P1043」数字游戏

    「洛谷P1043」数字游戏 日后再写 代码 /*#!/bin/sh dir=$GEDIT_CURRENT_DOCUMENT_DIR name=$GEDIT_CURRENT_DOCUMENT_NAME ...

  2. 「 洛谷 」P2768 珍珠项链

    珍珠项链 题目限制 内存限制:125.00MB 时间限制:1.00s 标准输入输出 题目知识点 动态规划 \(dp\) 矩阵 矩阵乘法 矩阵加速 矩阵快速幂 题目来源 「 洛谷 」P2768 珍珠项链 ...

  3. 「 洛谷 」P4539 [SCOI2006]zh_tree

    小兔的话 推荐 小兔的CSDN [SCOI2006]zh_tree 题目限制 内存限制:250.00MB 时间限制:1.00s 标准输入输出 题目知识点 思维 动态规划 \(dp\) 区间\(dp\) ...

  4. 「 洛谷 」P2151 [SDOI2009]HH去散步

    小兔的话 欢迎大家在评论区留言哦~ HH去散步 题目限制 内存限制:125.00MB 时间限制:1.00s 标准输入 标准输出 题目知识点 动态规划 \(dp\) 矩阵 矩阵乘法 矩阵加速 矩阵快速幂 ...

  5. 「P4994」「洛谷11月月赛」 终于结束的起点(枚举

    题目背景 终于结束的起点终于写下句点终于我们告别终于我们又回到原点…… 一个个 OIer 的竞赛生涯总是从一场 NOIp 开始,大多也在一场 NOIp 中结束,好似一次次轮回在不断上演.如果这次 NO ...

  6. 「洛谷 P1801」黑匣子

    好像很久没有更过博客了,因为博主这几周很忙.其实是在搞颓. 题意很难懂,所以就不重复了.其实是懒. 一眼看上去这是个 \(Splay\) 裸题,直接插入一个数,查询区间第 \(K\) 大,但是这样太不 ...

  7. Solution -「CTS 2019」「洛谷 P5404」氪金手游

    \(\mathcal{Description}\)   Link.   有 \(n\) 张卡牌,第 \(i\) 张的权值 \(w_i\in\{1,2,3\}\),且取值为 \(k\) 的概率正比于 \ ...

  8. 「洛谷4197」「BZOJ3545」peak【线段树合并】

    题目链接 [洛谷] [BZOJ]没有权限号嘤嘤嘤.题号:3545 题解 窝不会克鲁斯卡尔重构树怎么办??? 可以离线乱搞. 我们将所有的操作全都存下来. 为了解决小于等于\(x\)的操作,那么我们按照 ...

  9. 「洛谷3338」「ZJOI2014」力【FFT】

    题目链接 [BZOJ] [洛谷] 题解 首先我们需要对这个式子进行化简,否则对着这么大一坨东西只能暴力... \[F_i=\sum_{j<i} \frac{q_iq_j}{(i-j)^2}-\s ...

随机推荐

  1. eclipse和myeclipse的下载地址

    官方下载地址: Eclipse 标准版 x86 http://mirror.hust.edu.cn/eclipse//technology/epp/downloads/release/luna/R/e ...

  2. C# 连接Mysql 字符串

    Database=XXX;Data Source=XXX;User Id=XXX;Password=XXX;pooling=false;CharSet=utf8;port=3306

  3. valgrind详解

    调不尽的内存泄漏,用不完的Valgrind Valgrind 安装 1.valgrind 安装包下载地址:http://valgrind.org/downloads/repository.html(使 ...

  4. JPA entityManagerFactory配置详解

    以下是本人的一些理解 如有误的地方欢迎指出 谢谢! jpa.LocalContainerEntityManagerFactoryBean 与 hibernate的sessionFactory一样都实现 ...

  5. 列表控件JList的使用

    --------------siwuxie095                             工程名:TestUI 包名:com.siwuxie095.ui 类名:TestList.jav ...

  6. patch请求--501错误

    通过命令 tail -f /var/log/wildfly/wrapper.log -n 1000 查看控制台: 并没有报红. 未显示具体哪行代码有错误. debug运行patch请求,根本没有进入p ...

  7. WordCount编码与测试

    1. github项目地址:https://github.com/wwwwu/WordCount 2.PSP表格: PSP2.1 PSP阶段 预估耗时 (分钟) 实际耗时 (分钟) Planning ...

  8. HUST高级软件工程--测试管理工具实践--Day2

    测试管理工具实践--Day2 今天完成任务情况: 小靳 今天主要探索如何安装jira.在官网下载了最新版Windows64位jira,然后按照网上教程走,设置好端口号.在安装之前自以为这款软件在安装的 ...

  9. 小小c#算法题 - 12 - Joseph Circle(约瑟夫环)

    约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数(从1开始报数),数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又 ...

  10. 并发编程学习笔记之可见性&过期数据(二)

    想要使用多线程编程,有一个很重要的前提,那就是必须保证操纵的是线程安全的类. 那么如何构建线程安全的类呢? 1. 使用同步来避免多个线程在同一时间访问同一数据. 2. 正确的共享和安全的发布对象,使多 ...