前言

在考场被这个题搞自闭了,那个时候自己是真的太菜了。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. kvm iptables 3306端口

    # iptables -t nat -A PREROUTING -p TCP --dport 3306 -j DNAT --to-destination 192.168.122.102:3306# i ...

  2. 最全SDWebImage-3.8版本源码阅读详解

    一.前言 SDWebImage,非常友好的网络图片加载第三方框架,在GitHub中已经获得了15000++的star,链接地址:https://github.com/rs/SDWebImage 本人分 ...

  3. 根据URL下载文件

    commons-io 包中已经封装好了,直接可以使用 一.添加依赖 <dependency> <groupId>org.apache.commons</groupId&g ...

  4. Mysql Hive 通用的行列转换

    就是简单的一个字符串拼接,意义和所用场景自己体会下 insert into table agent_library1 select concat_ws(':',collect_set(name)) a ...

  5. 自定义Android Studio方法注释模板

    前言 你们从Eclipse转到Android Studio的时候,是不是会怀念Eclipse的方法注释模版? 敲/**加回车,模板就出来了,而Android Studio却不能自定义(或者我没有找到) ...

  6. ListView里面adapter的不同分类的item

    public class PlayAdapter extends BaseAdapter { /** * 标题的item */ public static final int ITEM_TITLE = ...

  7. [chmod]linux中给文件增加权限

    chmod命令 1.chmod u+x file.sh 2.sudo chmod 777  文件名 注: 如果给所有人添加可执行权限:chmod a+x 文件名:如果给文件所有者添加可执行权限:chm ...

  8. apache重写规则详解

    RewriteEngine on 为重写引擎开关,如果设为off,则任何重写规则定义将不被应用,该开关的另一好处就是如果为了临时拿掉重写规则,则改为off再重启动Apache即可,不必将下面一条条的重 ...

  9. IFC—IfcProduct实体继承框架

  10. TaikrSpaceShooterStartKit.unitypackage包下载地址

    有好多教程里面没有资源包,现在加密分享给大家 unity4.*  链接: https://pan.baidu.com/s/1XMo2zVpV3ZhkNZKOb6H0yw 密码: tqnt unity5 ...