1 条题解

  • 0
    @ 2025-8-24 21:20:23

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Panda_hu
    [已AFO]这个家伙不懒,但是什么也没能留下

    搬运于2025-08-24 21:20:22,当前版本为作者最后更新于2017-08-25 10:41:11,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题解

    step 1理解题意

    在做这道题之前,一定要理解好题意,有一个需要特别注意注意的地方:

    青蛙不是一定要跳到石头上[嗯...这一点坑了我好久]而是指青蛙尽量不踩石头的情况下还要跳到多少个石头上[语文渣求原谅]。

    step 2状态转移方程

    这是一个比较简单方程式。

    首先设f[i]为在i点上的最少踩石子数则在前面(i-s)到(i-t)的点都可以改变i点的值,因此我们可以取f[i-s]-f[i-t]之中的最小值,另外如果有石头就加上1,如果没有就不加值,这里我们直接用flag[i]表示该点有无石头(有则为1,无则为0)。

    因此我们可以写出状态转移方程式:f[i]=min(f[ij]+flag[i]s<=j<=t)f[i]=\min(f[i-j]+flag[i]|s<=j<=t)

    step 3路径压缩

    实际上,这题还没完呢...如果我们定义一个f[10^9]的数组,这肯定是会爆内存的——所以...[我就放弃了这道题][额,可能吗]..因此我们需要使用一种方法,使得这里采用一种最合适的方法——路径压缩(其实还有其他更(bu)优(kao)秀(pu)方法的),目的是要找到两石同相隔较长时直接缩短的方法。

    [前方高能,请数学学科恐惧症患者尽快撤离!!]:

    假设每次走p或者p+1步.我们知道gcd(p,p+1)\gcd(p,p+1)=1.

    由扩展欧几里得可知,对于二元一次方程组:

    px+(p+1)y=gcd(p,p+1)px+(p+1)y=\gcd(p,p+1)是有整数解的,即可得:px+(p+1)y=spx+(p+1)y=s是一定有整数解的。

    px+(p+1)y=spx+(p+1)y=s的解为:x=x0+(p+1)t,y=y0ptx=x0+(p+1)t,y=y0-pt。令0<=x<=p0<=x<=p(通过增减t个p+1来实现),s>p(p+1)1s>p*(p+1)-1,

    则有:$y=\frac{s-px}{p+1}>=\frac{s-p^2}{p+1}>\frac{p*(p+1)-1-px}{p+1}>=0$

    即表示,当 s>=p(p+1)s>=p*(p+1) 时,px+(p+1)y=spx+(p+1)y=s 有两个非负整数解,每次走p步或者 p+1p+1 步,p(p+1)p*(p+1) 之后的地方均能够到达。

    如果两个石子之间的距离大于 p(p+1)p*(p+1) ,那么就可以直接将他们之间的距离更改为 p(p+1)p*(p+1)

    综上,得到压缩路径的方法:若两个石子之间的距离> t(t1)t*(t-1) ,则将他们的距离更改为 t(t1)t*(t-1)

    因为 t<=10t<=10 ,因此我们可以直接将大于10*9的距离直接化为90.

    但是要注意,对于 s=ts=t 这种特殊情况,这种方法是不成立的应为在这种情况下,每次是不能够走p+1步的,因此需要另外特殊判断。

    方程如下:

    f[i]=f[i1]+(imods==0)f[i]=f[i-1]+(i \mod s ==0)

    代码实现

    #include<iostream> 
    #include<cstdio> 
    #include<algorithm> 
    #include<climits> 
    using namespace std; 
    int f[10005],far[10005],a[10005],flag[10005],p,s,t,n; 
    int main() 
    { 
        scanf("%d",&p); 
        scanf("%d%d%d",&s,&t,&n); 
        if(s==t) //特殊情况判断
        { 
            int cont=0,qaq; 
            for(int i=1;i<=n;++i)scanf("%d",&qaq),cont+=((qaq%s)==0); 
            printf("%d\n",cont);return 0; 
        } 
        for(int i=1;i<=n;i++)scanf("%d",&a[i]); 
        sort(a+1,a+n+1);a[0]=0;f[0]=0; 
        far[n+1]=min(p-a[n],100);p=0; //计算终点与最后一个点的距离
        for(int i=1;i<=n;i++)far[i]=min(a[i]-a[i-1],90),p+=far[i],flag[p]=1; //缩短路径,存储缩短后的终点距离并标记石头位置
        p+=far[n+1]; 
        for(int i=1;i<=p+9;i++) 
        { 
            f[i]=INT_MAX-1; 
            for(int j=s;j<=t;j++)if(i>=j)f[i]=min(f[i],f[i-j]+flag[i]); 
        } 
        int minn=INT_MAX-1; 
        for(int i=p;i<=p+9;i++) //因为青蛙可以跳出边界且t<=10因此再终点后p-p+9中取最小值
        minn=min(minn,f[i]); 
        printf("%d",minn); 
    } 
    

    CSDN

    2020.7.2 UPD:想不到这篇博客会收到这么多的好评...这篇博客是我初学动态规划的时候写的,并且当时也还没有考NOIP2017。所以存在很多理解不够深刻之处。

    本题需要我们需要求得一个最小的值tt使得对于所有s>ts>tpx+(p+1)y=spx+(p+1)y=s一定有非负整数解。根据NOIP2017提高组D1T1的结论,我们可以知道这个数为t=p(p+1)p(p+1)t=p(p+1)-p-(p+1)。 由于本题的最大步长为1010,因此tmax=9×10910=71t_{max}=9\times10-9-10=71。因此本题我们的压缩距离最小可以达到7171(在本文做法中不是7272)。

    先前对于同学们的误导,笔者深表歉意。

    • 1

    信息

    ID
    54
    时间
    1000ms
    内存
    128MiB
    难度
    4
    标签
    递交数
    1
    已通过
    1
    上传者