链接:

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld

题目描述

在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。

题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。

输入描述:

第一行有一个正整数L(1<=L<=109),表示独木桥的长度。
第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1<=S<=T<=10,1<=M<=100。
第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。 所有相邻的整数之间用一个空格隔开。

输出描述:

只包括一个整数,表示青蛙过河最少需要踩到的石子数。

示例1
输入

10
2 3 5
2 3 5 6 7

输出

2

备注:
对于30%的数据,L<=10000;

对于全部的数据,L<=10^9^。

题解:

首先肯定是想到dp
dp[i]表示走到i这个位置的最小的踩石头的次数
当从位置j调到位置i后,
如果当前位置i没有石头 f [ i ] = min ( f [ i ] , f [ j ] )
如果当前位置i有石头:f[i] = min ( f [ i ], f [ j ] + 1)
就就这样吗?
并不是,看看数据范围,开数组肯定爆了,这是在逼我们做优化
我注意看除了L之外的其他数据,相比之下S,T,M都够小的,像S,T才0到10,那两个石头之间距离很大的话,我们可以调整步伐使得不踩到石头如果这样考虑,两个距离很远的石头之间,我们可以到达任意距离。那距离再长又有何影响。
那我就要考虑两个石头之间距离达到多少才算大呢?
[S,T]是跳跃的范围
当距离Len大于S * T时,len都至少有两种方法可以跳到,(因为S * T等于T个S相加或者S个T相加),之后的距离都可以在修改之前的基础上得到实现。说白了就是距离大于ST之后,都可以到达,那我们把距离大的都缩小到ST,这样L的范围不就限制住了嘛
回归本题,S和T不重复时,S最大是9,T最大是10,S * T最大是90,也就是距离大于90的两个石头
所以我们只需将每两个石头超过 S * T 的距离缩成S * T就可以了

当S=T时,只需要枚举每个石头的坐标是否为s的倍数即可

代码

含注释

#include<bits/stdc++.h>
using namespace std;
const int maxn = 90 * 105;


int s[maxn],a[maxn];
int dp[maxn],len;

bool f[maxn]; //标记改点是否为石头 
memset(dp,0x7f,sizeof(dp));
int main()
{
    int L,s,t,m;
    cin >> L >> s >> t >> m;
    len = s * t;
    for(int i = 1 ; i <= m ; ++ i)cin >> s[i];

    sort(s + 1,s + 1 + m);

    if(s == t)//当s与t重复时 
    {
        int sum =0;
        for(int i = 1 ; i <= m ; ++ i)
            if(s[i] % s == 0)sum++;

        printf("%d",sum);
        return 0;
    }
    for(int i = 1 ; i <= m ; ++ i)
    {
        int d = s[i] - s[i - 1];//两个石头之间的距离 

        if(d >= len)d = len;//过大的距离进行更新 

        a[i] = a[i - 1] + d;//将新的石头之间的距离更新到数组a中 

        f[a[i]] = 1;//更新新的石头 


    }
  if(i==m)L = a[m] + 1;//根据最后一个石头来重新更新独木桥的长度 
    dp[0] = 0;

    for(int i = 1 ; i <= L ; ++ i)
        for(int j = s ; j <= t ; ++ j)
        {
            if(i >= j)
            {
                if(f[i])dp[i] = min(dp[i - j] + 1,dp[i]);
                else dp[i] = min(dp[i - j],dp[i]);
            }
        }

    int ans = 200;
    for(int i = a[m] ; i <= L ; ++ i)
        ans = min(ans,dp[i]);
    cout << ans << endl;
    return 0;
}