题解 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,我们还是按照常规思路,想一想有哪些地方是多余的,可以优化掉。

可以观察到题目中\(1\leq S \leq T \leq 10, 1\leq M\leq 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;
}

猜你喜欢

转载自www.cnblogs.com/lcezych/p/12114219.html