[线性DP] 洛谷1052 过河(线性离散化)(输入数据处理问题)

题目

LP1052

思路

可以看到本题的l很大,不能作为数组索引量。一般情况下考虑两种做法,一种是转变思考状态的方法,把l转移到数组元素值上,另一种离散化。
本题用后者。
首先列出状态转移方程,i指的是线段上的坐标。

d ( i ) = m i n { d ( i k ) | k [ s , t ] }



(一次跳跃的距离是 S 到 T 之间的任意正整数,包括S和T)
接下来说离散化。本题不同于普通的点离散化,因为要判断几个数([s,t])的公倍数能否到达或跨越一个石子,到达的话直接是石子的坐标,跨越的话会有一个“缓存区域”,这个缓存区域指由青蛙跳的步数的组合,第一次跨越石子点的坐标集合。这个集合一定是以一个石子的坐标开头,但终点不知道。如果我们知道终点,那么我就可以将任何两个石子之间距离大于这个缓存区域长度的,缩减为这个长度,从而实现离散化的目的。
总结了一下大佬们确定这个终点的三种方法:
1.lca:终点不会大于lca(1,…,10)=2520。
2.终点不会大于,9和10不能凑成的整数最大值,参考noip2017d1t1,这个值是9*10-9-10=71。
3.若缓存区域长度d大于t,直接d%t,因为d一定可以有d%t一步跳过来。(最优,状态总数为2*t*m)


输入数据处理问题
注意:本题的输入数据没有明确告知输入排序好了,而样例显然是排好的来骗你。而本题的 所有推理都建立在石子坐标从小到大排好的基础上,所以一定要对输入数据进行第一次的处理。

代码

本题代码需要注意,线性离散化的通常实现方法。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;

const int maxm = 100 + 10;
const int maxl = 2000 + 100; // 2*t*m
int l, s, t, m, stone[maxm], pos[maxm], d[maxl];

int main() {
    scanf("%d%d%d%d", &l, &s, &t, &m);
    _rep(i, 1, m) scanf("%d", &stone[i]);
    sort(stone + 1, stone + m + 1);   // 注意,实际比赛中,这种对数据的简单操作往往十分重要

    // 离散化
    _rep(i, 1, m)
        if (stone[i] - stone[i - 1] > t)  pos[i] = pos[i - 1] + t + (stone[i] - stone[i - 1]) % t;
        else pos[i] = pos[i - 1] + stone[i] - stone[i - 1];

    if (l - stone[m] > t) l = pos[m] + t + (l - stone[m]) % t;
    else l = pos[m] + l - stone[m];

    // 递推
    int stoneindex = 1;
    _for(i, 1, l + t) {
        int add = 0;
        if (pos[stoneindex] == i) { add = 1; stoneindex++; }
        d[i] = maxm;
        _rep(k, s, t)
            if (i - k >= 0)
                d[i] = min(d[i], d[i - k] + add);
    }

    // 找解,输出
    int ans = maxm;
    _for(i, l, l + t) ans = min(ans, d[i]);
    printf("%d\n", ans);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/icecab/article/details/81067664