NOIP 2005 提高组 复赛 river 过河

来源:互联网 发布:情义我心知黎明 编辑:程序博客网 时间:2024/05/29 19:44

NOIP 2005 提高组 复赛 river 过河

1.样例模拟了,发现无论是从前到后,还是从后到前,都有多种可能。

2.当然,程序没什么头绪,选什么为状态?

3.对路径压缩心存疑虑,看了这篇,https://zhidao.baidu.com/question/137697462.html感觉好多了,摘抄如下:

以前做过,我证明一下:好像ST最大值分别为10,那么我们尽量让ST不重合,也就是S=9,T=10,来看一下,下面是可以跳到的序列:0, 9,10, 18,19,20, 27,28,29,30, 36,37,38,39,40 ……90,91,92,93,94,95,96,97,98,99,100.注意到在前面会有断开的区间,也就是状态不同,而81以后青蛙可以调到任何一个格,所以可以压缩到100.至于为什么不压缩到更低,是因为前面那些便于你理解,实际上是这个公式算的:lcm(max(T),max(T)-1)证明复杂从略。

4.https://zhidao.baidu.com/question/69031147.html也讲得不错。

摘抄如下:

动态转移方程很简单,主要是状态压缩

如果两块石头A和B,AB的距离超过2520,则让B及其后的石头的位置每次减2520,直到AB的距离小于2520.(PS:2520为1-10的最小公倍数)
这样一来路径就能减小为260000以下.
然后DP每个点就OK了.也不用考虑S=T的情况.程序极短
几个注意点:
1.若最后一个石头的位置离终点很远,那么将终点位置提前,方法一样.
2.有些地方不能到达,则可以将其石头数顶为101,这样S=T也不会错.

其实如果不考虑s=t的话,两点大于100的可以压到100,甚至20,某人用压到10仅错了1组数据。。。

5.http://blog.csdn.net/zzp441524586/article/details/7654269讲得很棒

石子稀疏对我们解题有什么帮助呢?我们来看一下下面的推断:

第一种情况:      

      当s=t时,很简单,青蛙踩到的石头肯定是s的倍数,那么只要统计一下所有石子中有多少是s的倍数,输出即可。

第二种情况:s<t

我们先来看一组数据。s=4t=5


从数据中我们看到,12以后的点全部都是可以到达的了。如果s=4,t=5,在一段100000的距离中没有石头,其实12以后的点都是不用递推就知道肯定能到达的。那么我们用原始的方法做就会浪费很大的资源。

        所以当s=4,t=5时,如果一段没有石头的区间长度在4*5=20以外,那么我们只要递推前20就可以了,因为20后面的情况是一样的(仔细想想为什么?)。

假设s=3,t=5,那么11=3+4+4就也可以到达了。所以,只有当t=s+1时,连续的点的起始位置才能尽量后面。最坏情况就是s=9,t=10了(仔细想想为什么?)。

       跟前面的s=4,t=5的情况一样,其实s=9,t=10时只要一段没有石头的区间长度在90之外,我们都把它当做90对待就可以了。

        因此,我们每次对于一段没有石头的区间长度为x,如果x<=t(t-1),我们仍然把它当做x来处理;相反,当x>t(t-1)时,我们就把它当做t(t-1)处理。这样,最大的复杂度就是t(t-1)*(石头个数+1)=90*101=9090,比之前的复杂度大大降低。

        这种方法叫状态压缩,我们这题用的方法叫离散化。至此,过河完美AC!

6.有了上面的基础后,那么这篇文章就容易看懂了,http://blog.csdn.net/yuyanggo/article/details/48341259摘抄如下:

桥很长,但是石子数很少,也就是说,中间可能存在很长的一段空白区域,而这段空白区域就是造成大量无效运算的元凶,需要我们将这部分空白区域进行压缩。

         现在,我们假设每次走p或者p+1步,则有 px+(p+1)y=s。

         1.gcd(p+1,p)=gcd(1,p)=1,即p与p+1的最大公约数是1;

         2.由扩展欧几里得可知,对于二元一次方程组:px+(p+1)y==gcd(p,p+1)是有整数解的,即可得:

           px+(p+1)y==s是一定有整数解的。

         假设px+(p+1)y==s的解为:x=x0+(p+1)t,y=y0-pt。令0<=x<=p(通过增减t个p+1来实现),s>p*(p+1)-1,则有:y=(s-px)/(p+1)>=(s-p*p)/(P+1)>(p*(p+1)-1-px)/(p+1)>-1>=0

          即表示,当s>=p*(p+1)时,px+(p+1)y==s有两个非负整数解,每次走p步或者p+1步,p*(p+1)之后的地方均能够到达。如果两个石子之间的距离大于p*(p+1),那么就可以直接将他们之间的距离更改为p*(p+1)。

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

7.//p1052 过河
//提交,测试点2,6 RE,测试点3,7,10 WA
//for(i=1;i<=m;i++)//1 此处漏了循环,提交测试点2,6Re 测试点7WA
//#define maxn 110//2 此处写成 #define maxn 100+10 提交AC
#include <stdio.h>
#include <string.h>
#define maxn 110//2 此处写成 #define maxn 100+10
#define inf 999999999
int a[maxn],f[maxn*maxn],stone[maxn*maxn];
int min(int a,int b){
    if(a>b)
        return b;
    return a;
}
int main(){
    int l,s,t,m,i,j=0,d,k,ans=0,b;
    memset(a,0,sizeof(a));
    for(i=0;i<maxn*maxn;i++)
        f[i]=inf;
    memset(stone,0,sizeof(stone));
    scanf("%d%d%d%d",&l,&s,&t,&m);
    for(i=1;i<=m;i++)
        scanf("%d",&a[i]);
    if(s==t){
        for(i=1;i<=m;i++)//1 此处漏了循环
            if(a[i]%s==0)
                ans++;
        printf("%d\n",ans);
        return 0;
    }
    for(i=1;i<=m;i++)//自小到大
        for(j=i+1;j<=m;j++)
            if(a[i]>a[j]){
                b=a[i];
                a[i]=a[j];
                a[j]=b;
            }
    k=s*t;
    for(j=0,i=1;i<=m;i++){
        a[i]-=j;
        d=a[i]-a[i-1];
        if(d>k){
            a[i]=a[i-1]+k;
            j+=d-k;
        }
    }
    for(i=1;i<=m;i++)
        stone[a[i]]=1;
    f[0]=0;
    for(i=0;i<=a[m];i++)
        for(j=s;j<=t;j++)
            if(stone[i+j]==1)
                f[i+j]=min(f[i+j],f[i]+1);
            else
                f[i+j]=min(f[i+j],f[i]);
    ans=inf;
    for(i=1;i<t;i++)
        ans=min(ans,f[a[m]+i]);
    printf("%d\n",ans);
    return 0;
}

2017-5-11 23:50


0 0