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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SSL_hzb/article/details/84445091

题意

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

思路

一看到这个题就觉得是动态规划,不过水平有限,考场上没做出来,之后看了题解才会做的。
我们可以把时间看成一个数轴,如这个洛谷上题解的图
在这里插入图片描述
把这个数轴分成若干的长度 M \geq M 的段,那么等待时间总和就为每一段上所有点到这一段右端点的距离总和。代表从右边这个端点开始出发。那么我们设 f [ i ] f[i] 为设表示对数轴分段,且最后一段的右边界是i,数轴内的点到各自所属段右边界的距离之和最小值。可得动态转移方程:
f [ i ] = m i n j < i m ( f [ j ] + j < t k i i t k ) f[i]=\underset {j<i-m}{min}(f[j]+\sum_{j<t_k \leq i}^{ }i-t_k)
对这个 j < t k i i t k \underset{j<t_k \leq i}{\sum}i-t_k ,我们可以把它拆,变成 ( p i p j ) i ( s i s j ) (p_i-p_j)*i-(s_i-s_j) ,其中 p i p_i 为前 i i 个时间上有几个点, s i s_i 为前i个时间上每个 t i t_i 的总和。
这样的做法有50分。
我们可以发现,如果 i = 6 m = 2 i=6,m=2 时, f 2 f_2 可以转移到 f 4 f 4 f_4,f_4 又可以转移到 f 6 f_6 。那么当 j i 2 m j \leq i-2m 时,可以转移到 i m i-m ,由于分段数不会影响答案,所以我们可以从 i 2 m + 1 i-2m+1 转移,避免冗余操作,这优化可以得到70分。
大佬题解还给了个优化:

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

那么避免漏解,我们让 f [ i ] = f [ i m ] f[i]=f[i-m]
目标 f [ i ] f[i] { i > = M i>=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);
}

猜你喜欢

转载自blog.csdn.net/SSL_hzb/article/details/84445091
今日推荐