版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SSL_hzb/article/details/84445091
题意
有 个人要坐车,已知车往返一趟要 分钟,还有每个同学去等车的时间,求出最少的等待时间总和。
思路
一看到这个题就觉得是动态规划,不过水平有限,考场上没做出来,之后看了题解才会做的。
我们可以把时间看成一个数轴,如这个洛谷上题解的图
把这个数轴分成若干的长度
的段,那么等待时间总和就为每一段上所有点到这一段右端点的距离总和。代表从右边这个端点开始出发。那么我们设
为设表示对数轴分段,且最后一段的右边界是i,数轴内的点到各自所属段右边界的距离之和最小值。可得动态转移方程:
对这个
,我们可以把它拆,变成
,其中
为前
个时间上有几个点,
为前i个时间上每个
的总和。
这样的做法有50分。
我们可以发现,如果
时,
可以转移到
又可以转移到
。那么当
时,可以转移到
,由于分段数不会影响答案,所以我们可以从
转移,避免冗余操作,这优化可以得到70分。
大佬题解还给了个优化:
举个例子,假设正在求 ,但在 ( , ] 中没有任何点,这个 相对来说就是 “无用” 的。原因是若最后一段长度恰好 ,这里面又没有任何点,不分割也罢。长度 时,完全可以把这一段的右边界往左“拖”,产生不劣的答案。
那么避免漏解,我们让
。
目标
{
}
代码
#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);
}