题解 P1052 过河

Posted lcezych

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 P1052 过河相关的知识,希望对你有一定的参考价值。

题目

在某人的挑唆之下来做了这道题,说是路径压缩dp,然鹅我做完以后并没有发现什么和路径压缩有关的东西。

题目的意思大概就是给你一条数轴和一些石子,再给你每一次可以往前走的长度区间,求想要走完这条数轴最少需要经过多少个石子。

首先对于30%的数据,(L leq 10000),大概就是一个线性dp。

不难想到对于每一个点(i),都可以由([i-s,i-t])转移过来。

(f[i])表示在点(i)时的最小答案,那么我们就有了一个最基本的转移方程:(f[i]=min(f[k]))

当然如果(i)处有石子,(f[i]++)

这样是从后往前推的,也可以从前往后。

大概就是这样:

for(int i = 0; i < L; i ++)
    {
        if(a[i] == 1) f[i] += 1;
        for(int j = s; j <= t; j ++)
        {
            if(i + j >= l) ans = min(ans, f[i]);
            f[i + j] = min(f[i], f[i + j]);
        }
    }

100%的数据:(L leq 10^9)

显然这种情况如果直接开(L)的数组会MLE,我们还是按照常规思路,想一想有哪些地方是多余的,可以优化掉。

可以观察到题目中(1leq S leq T leq 10, 1leq Mleq 100)

也就是说每一次走不超过十步,一共不超过100个石子。

显然这种情况下两个石子之间的距离会非常的长,并且要走的步数也非常的多。而这些步数中绝大多数都是没有用的,那么一个自然的想法就是把两个石子之间的距离缩短(难道这就是传说中的路径压缩?)

先从最简单的想起,假如一次只能走(S)或者(T)步,从原点开始走,那么显然一定可以走到(lcm(S,T))这个位置。在这种情况下,如果((0, lcm(S,T)])之间没有石子,那么((0,lcm(S,T)])就是无用的,因为它不会对答案产生任何影响,可以把这一段删去。

推广一下就可以知道,从点(i)开始,一次走([S,T])步,一定可以到达(i+lcm(S...T))这个点

再利用上面的结论,就可以对两点之间的距离进行缩短

核心代码:

for(int i = 1; i <= n; i ++) 
    {
        if(b[i] - last >= mod) b[i] = last + (b[i] - last) % mod;
        a[b[i]] = 1;
        last = b[i];
    }

完整代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int L = 5000005;
int n, s, t, l, b[L], a[L], f[L], mod;
int main()
{
    scanf("%d", &l);
    scanf("%d%d%d", &s, &t, &n);
    mod = 10;//这里是为了增加间隔 
    for(int i = s; i <= t; i ++) mod  *= i;
    memset(f, 0x3f, sizeof(f));
    int ans = 0x3f3f3f3f, last = 0; f[0] = 0;
    for(int i = 1; i <= n; i ++) scanf("%d", &b[i]);
    sort(b + 1, b + 1 + n);
    for(int i = 1; i <= n; i ++) 
    {
        if(b[i] - last >= mod) b[i] = last + (b[i] - last) % mod;
        a[b[i]] = 1;
        last = b[i];
    }
    if(l - last >= mod) l = last + (l - last) % mod;
    for(int i = 0; i < l; i ++)
    {
        if(a[i] == 1) f[i] += 1;
        for(int j = s; j <= t; j ++)
        {
            if(i + j >= l) ans = min(ans, f[i]);
            f[i + j] = min(f[i], f[i + j]);
        }
    }
    for(int i = 1; i <= n; i ++) cout<<b[i] << endl;
    cout << ans;
}

以上是关于题解 P1052 过河的主要内容,如果未能解决你的问题,请参考以下文章

P1052 过河

P1052 过河

Luogu P1052 过河 DP

P1052 [NOIP2005 提高组] 过河(dp&路径压缩)

洛谷 P1052过河

洛谷P1052 过河 动态规划