题意
有NNN个人要坐车,已知车往返一趟要mmm分钟,还有每个同学去等车的时间,求出最少的等待时间总和。
思路
一看到这个题就觉得是动态规划,不过水平有限,考场上没做出来,之后看了题解才会做的。
我们可以把时间看成一个数轴,如这个洛谷上题解的图

把这个数轴分成若干的长度≥M\geq M≥M的段,那么等待时间总和就为每一段上所有点到这一段右端点的距离总和。代表从右边这个端点开始出发。那么我们设f[i]f[i]f[i]为设表示对数轴分段,且最后一段的右边界是i,数轴内的点到各自所属段右边界的距离之和最小值。可得动态转移方程:
f[i]=minj<i−m(f[j]+∑j<tk≤ii−tk)f[i]=\underset {j<i-m}{min}(f[j]+\sum_{j<t_k \leq i}^{ }i-t_k)f[i]=j<i−mmin(f[j]+j<tk≤i∑i−tk)
对这个∑j<tk≤ii−tk\underset{j<t_k \leq i}{\sum}i-t_kj<tk≤i∑i−tk,我们可以把它拆,变成(pi−pj)∗i−(si−sj)(p_i-p_j)*i-(s_i-s_j)(pi−pj)∗i−(si−sj),其中pip_ipi为前iii个时间上有几个点,sis_isi为前i个时间上每个tit_iti的总和。
这样的做法有50分。
我们可以发现,如果i=6,m=2i=6,m=2i=6,m=2时,f2f_2f2可以转移到f4,f4f_4,f_4f4,f4又可以转移到f6f_6f6。那么当j≤i−2mj \leq i-2mj≤i−2m时,可以转移到i−mi-mi−m,由于分段数不会影响答案,所以我们可以从i−2m+1i-2m+1i−2m+1转移,避免冗余操作,这优化可以得到70分。
大佬题解还给了个优化:
举个例子,假设正在求 fif_ifi,但在 (i−mi-mi−m,iii] 中没有任何点,这个 fif_ifi相对来说就是 “无用” 的。原因是若最后一段长度恰好 =m=m=m,这里面又没有任何点,不分割也罢。长度>m>m>m时,完全可以把这一段的右边界往左“拖”,产生不劣的答案。
那么避免漏解,我们让f[i]=f[i−m]f[i]=f[i-m]f[i]=f[i−m]。
目标f[i]f[i]f[i]{i>=Mi>=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);
}

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

被折叠的 条评论
为什么被折叠?



