• 题解 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;
    }
    
  • 相关阅读:
    python基础#1
    shell脚本基础练习题
    shell计算100以内加法
    shell脚本添加用户
    python学习ing
    框架
    前端
    python基础之数据类型-面向对象
    python四种列表的插入方法及其效率
    Charles高阶操作
  • 原文地址:https://www.cnblogs.com/lcezych/p/12114219.html
Copyright © 2020-2023  润新知