【动态规划】洛谷_5017 摆渡车

本文探讨了一道关于动态规划的问题,旨在寻找N个人在车往返一趟需m分钟条件下的最小等待时间总和。通过将时间轴划分并优化动态转移方程,实现了对冗余操作的避免,最终给出了详细的代码实现。

题意

NNN个人要坐车,已知车往返一趟要mmm分钟,还有每个同学去等车的时间,求出最少的等待时间总和。

思路

一看到这个题就觉得是动态规划,不过水平有限,考场上没做出来,之后看了题解才会做的。
我们可以把时间看成一个数轴,如这个洛谷上题解的图
在这里插入图片描述
把这个数轴分成若干的长度≥M\geq MM的段,那么等待时间总和就为每一段上所有点到这一段右端点的距离总和。代表从右边这个端点开始出发。那么我们设f[i]f[i]f[i]为设表示对数轴分段,且最后一段的右边界是i,数轴内的点到各自所属段右边界的距离之和最小值。可得动态转移方程:
f[i]=minj&lt;i−m(f[j]+∑j&lt;tk≤ii−tk)f[i]=\underset {j&lt;i-m}{min}(f[j]+\sum_{j&lt;t_k \leq i}^{ }i-t_k)f[i]=j<immin(f[j]+j<tkiitk)
对这个∑j&lt;tk≤ii−tk\underset{j&lt;t_k \leq i}{\sum}i-t_kj<tkiitk,我们可以把它拆,变成(pi−pj)∗i−(si−sj)(p_i-p_j)*i-(s_i-s_j)(pipj)i(sisj),其中pip_ipi为前iii个时间上有几个点,sis_isi为前i个时间上每个tit_iti的总和。
这样的做法有50分。
我们可以发现,如果i=6,m=2i=6,m=2i=6m=2时,f2f_2f2可以转移到f4,f4f_4,f_4f4f4又可以转移到f6f_6f6。那么当j≤i−2mj \leq i-2mji2m时,可以转移到i−mi-mim,由于分段数不会影响答案,所以我们可以从i−2m+1i-2m+1i2m+1转移,避免冗余操作,这优化可以得到70分。
大佬题解还给了个优化:

举个例子,假设正在求 fif_ifi,但在 (i−mi-mim,iii] 中没有任何点,这个 fif_ifi相对来说就是 “无用” 的。原因是若最后一段长度恰好 =m=m=m,这里面又没有任何点,不分割也罢。长度&gt;m&gt;m>m时,完全可以把这一段的右边界往左“拖”,产生不劣的答案。

那么避免漏解,我们让f[i]=f[i−m]f[i]=f[i-m]f[i]=f[im]
目标f[i]f[i]f[i]{i&gt;=Mi&gt;=Mi>=M}

代码

#include<cstdio>
#include<algorithm>

const int MAXL = 4000001;
int point[MAXL], sum[MAXL], f[MAXL];
int N, M, t, ans;

int main() {
    scanf("%d %d", &N, &M);
    for (int i = 1, x; i <= N; i++) {
        scanf("%d", &x);
        point[x]++;
        sum[x] += x;
        t = std::max(t, x);
    }
    for (int i = 1; i <= t + M; i++) {
        point[i] += point[i - 1];
        sum[i] += sum[i - 1];
    }
    ans = 2147483647;
    for (int i = 0; i <= t + M; i++) {
        if (i >= M && point[i] == point[i - M]) {//无点情况
            f[i] = f[i - M];
            continue;
        }
        f[i] = point[i] * i - sum[i];
        for (int j = std::max(0, i - M * 2 + 1); j <= i - M; j++)//除去冗余操作
            f[i] = std::min(f[i], f[j] + (point[i] - point[j]) * i - (sum[i] - sum[j]));
        if (i >= t) ans = std::min(ans, f[i]);
    }
    printf("%d", ans);
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值