• 2016.8.25 NOIP2012 day1 解题报告


    考试总结:

    1.  显然第一道送分题,我差一点直接打表来对拍了,后来发现我们的pdf有问题不能复制,并且我没有YJQ大神那样足够的时间每道题都对拍,就只能试过样例就遁,(其实这种密码我玩过,密码学入门密码,当时好像叫凯撒密码233);对了,ASCII的掌握也很重要,我之前一直以为大写在小写后面啧。(之间漏掉过大于以后减的步骤,这种简单题还是做少了居然耗了30min,以后好好检查争取一次过);

    2.  这道题我拿到的第一反应就是贪心,马上想了一个贪心规则但自己总觉得是错的,花了很长的时间举反例举不出来,后来和第三道题换着做都没做完快疯了,然后发现我无法证伪,但是它本来就是正确的啊…所以说以后要多从正面,特别是反证的方面思考问题;最后写出来以后输出不对,才发现是前面不包括自己,当时懵比因为那样的话我的方法就错了,然后突然发现两边都乘以一个lef就一样了,还是一种很巧妙的思维转化吧,然后得出了正确的算法,当然由于畏难我没有写高精度,后来发现其实很简单,就几行,所以不要畏难,熟练掌握模板(对了,还有个技巧是sort排序可以各种写cmp,对于排序的灵活性非常有用;

    3.  本来打算用DP啊背包啊记忆啊什么的,所以还是基本概念掌握不清晰,很明显这些算法不能用;后来发现就求min1,min2的时间复杂度最高,然后一个快排就可以解决问题(当然,要写很多个if很麻烦),最后还是决定拿70分走,然后发现太难调了,临交卷还没调出来orz.)

    4.  总的来说,这套题考得排名不错,但我认为不是很理想,第二题证伪的时间太长,没有把握好时间,没有想到反证,高精度没敢写,最后一道题没有先检查再调试,导致很多一眼看出来的错调了很久,要站在逻辑的高度上。代码实现能力有待提高,60多行的程序应该写得心平气和思路清晰才对;第二三题都还是很有价值(第二题的证明,第三题的错orz);

    解题报告:                                                   

    一.   Vigenère密码

    1.题意:通过按位的方式把暗文按照密钥翻译成明文,每个明文和密钥对应一个暗文;数据范围:密钥100,暗文1000;

    2.注意:

    1).是把暗文翻译成明文,害得我总觉得样例有问题,一定要仔细读题;

    2).题目要求明文大写暗文保持大写,密钥大小写无关紧要;

    3.分析:这道题的第二个注意揭示了,这是一道需要很多个if的题,只要考虑清楚,这道题就没问题;思路就是用ASCII码的运算,把密钥当做加法,a加0,b加1…

    4..代码:

    #include<iostream>

    #include<cstdio>

    #include<cstring>

    using namespace std;

    int lena,lenb;

    char a[105],b[1005];//meaning

    int main()

    {

    freopen("vigenere.in","r",stdin);

    freopen("vigenere.out","w",stdout);

    scanf("%s",a);getchar();scanf("%s",b);lena=strlen(a);lenb=strlen(b);

    for(int i=0;i<lenb;i++){

            int tmp=b[i];tmp-=a[i%lena];if(a[i%lena]<='Z')tmp+='A';else tmp+='a';

            if(b[i]<='Z'&&tmp<'A')tmp+=26;//ditails;

            else  if(b[i]>'Z'&&tmp<'a')tmp+=26;

            b[i]=tmp;

    }

    printf("%s",b);

    return 0;

    }//对了,用scanf一定记得注意换行符;

    二.   国王游戏

    1. 题意:给定一个第一位不能动的数列,后面的得分为之前所有的左元素的乘积除以自己的右元素,使最大得分最小;

    数据范围:对于20%的数据,有1≤n≤10,0 < a、b <8;

    对于40%的数据,有1≤n≤20,0 < a、b < 8;

    对于60%的数据,有1≤n≤100;

    对于60%的数据,保证答案不超过109;

    对于100%的数据,有1 ≤n ≤1,000,0 < a、b < 10000。

    1. 注意:

    1)      是前面的所有人,不包括自己!

    2)      <=10000的 1000次方应该是最多4000位,但如果是五位数就是5000了;

    1. 分析:要最大最小就对r*l从小到大排序,然后直接运算就行了;

    算法证明:

    1).基本思路:r*=l之后,本题就可以看作最左边包括自己的积除以r*l,左边的乘积一定是单增的,则除数也要单增,且单独看最后一个,被除数肯定是最大的,那么除数也要尽量大;

    2)正确性证明:当时决定采用这个算法时已经没有时间了,其实即使不r*=l左边也是单增,但是r*=l和直接排r肯定是不一样的,所以这里还涉及到反证的思想;

    ①.不相等时:不相等时,前后交换,后面的那个数和之前在后面的那个数面对的被除数是一样的,也就是说当后面的元素确定后交换不影响后一个位置的除数,然后被除数一样,交换后除数反而小了,所以得到的数肯定比之前相同除数被除数小的情况大,也比之前相同被除数除数大的情况大,最大值即使不会变大,也不可能变小;

    ②。相等时:永远要记住讨论相等,交换后,后一个数除数没变被除数没变,把l大的放在后面的话,前面的数被除数小了除数没变,至少不会更大,但是前面被除数至少不会更大,除数又相等,所以至少不会让最大值变化(因为后一个要么相等要么更大),所以相等时可以随意;

    (啊证明这种东西感觉对自己的逻辑学很有帮助);

        4.  代码:#include<iostream>

    #include<cstdio>

    #include<algorithm>

    #include<cstring>

    using namespace std;

    int maxn[5005],n,tmp[5005],ans[5005];

    struct per

    {

           int l,r;

    }a[1005];//条件

    bool cmp(per x,per y)

    {

           if(x.r*x.l<y.r*y.l)return true;

           return false;

    }

    void chengf(int u)

    {

           memset(tmp,0,sizeof(tmp));//高乘单!

           for(int i=1;i<=5003;i++){

                  tmp[i]+=maxn[i]*a[u].l;tmp[i+1]+=tmp[i]/10;tmp[i]%=10;

           }

           for(int i=1;i<=5004;i++)maxn[i]=tmp[i];

    }

    void chuf(int u)

    {

           memset(tmp,0,sizeof(tmp));int flg=0,cnt=0;

           for(int i=5004;i>=1;i--){

                  tmp[i]=(cnt*10+maxn[i])/a[u].r;

                  cnt=(cnt*10+maxn[i])%a[u].r;

           }

           for(int i=5004;i>=1;i--)if(tmp[i]>ans[i]){flg=1;break;}

           if(flg)for(int i=5004;i>=1;i--)ans[i]=tmp[i];

    }

    int main()

    {

           freopen("game.in","r",stdin);

           freopen("game.out","w",stdout);

           scanf("%d",&n);

           for(int i=1;i<=n+1;i++)scanf("%d %d",&a[i].l,&a[i].r);

           sort(a+2,a+n+2,cmp);maxn[1]=a[1].l;

           for(int i=2;i<=n+1;i++){      

               chuf(i);

                  chengf(i);

           }

           int tail=4004;while(!ans[tail])tail--;//虽然不用改,但没改完太好笑了

           for(int i=tail;i>=1;i--)printf("%d",ans[i]);

           return 0;

    }

    三.   开车旅行

    1.题意:A先开,去第二小的,B开去第一小的,AB轮流,不能到或者取不到停止,求给定x,A,B开的距离比值最小的sta以及给定sta,x(能开的距离),AB开的距离;

    数据范围:对于30%的数据,有1≤N≤20,1≤M≤20;

    对于40%的数据,有1≤N≤100,1≤M≤100;

    对于50%的数据,有1≤N≤100,1≤M≤1,000;

    对于70%的数据,有1≤N≤1,000,1≤M≤10,000;

    对于100%的数据,有1≤N≤100,000,1≤M≤10,000,-1,000,000,000≤Hi≤1,000,000,000,0≤X0≤1,000,000,000,1≤Si≤N,0≤Xi≤1,000,000,000,数据保证Hi互不相同。

    2.注意:

    1).距离为海拔高度差,距离相同,海拔低的近;

    2)对于第一问,比值相同输出海拔高的;

           3.模拟70分,先读取时遍历前面求min1,min2的值和端点(也可以只存端点),然后像链表一样走就行了,这道题正解是集合(或者用双向链表且走完删除也可以)+倍增(其实确定状态(包括谁开)以后就是唯一路径的树了),还没写;

           4.代码:

    (70)

    #include<iostream>

    #include<cstdio>

    #include<cmath>

    #include<cstring>

    using  namespace std;

    int n,a[100005],min1[100005][2],min2[100005][2],x,maxn,m,sta;

    int main()

    {

           freopen("drive.in","r",stdin);

           freopen("drive.out","w",stdout);

           memset(min1,127,sizeof(min1));memset(min2,127,sizeof(min2));

           scanf("%d",&n);for(int i=1;i<=n;i++){

           scanf("%d",&a[i]);for(int j=1;j<i;j++){

           int tmp= abs(a[i]-a[j]);

           if(tmp<min1[j][0]||(tmp==min1[j][0]&&a[i]<a[j])){

           min2[j][1]=min1[j][1];min2[j][0]=min1[j][0];

           min1[j][0]=tmp;min1[j][1]=i;}

           else if(tmp<min2[j][0]||(tmp==min2[j][0]&&a[i]<a[j])){

           min2[j][0]=tmp;min2[j][1]=i;}

           }}

           scanf("%d",&x);long long maxna=1,maxnb=0;//不能被覆盖;

           for(int i=1;i<=n;i++){//边界处理;

                  long long flg=i,cnt=0,ansa=0,ansb=0;

                  while(flg<=2e9&&flg!=n){

                  if(!cnt){

                      if(ansa+ansb+min2[flg][0]>x)break;

                         ansa+=(long long)min2[flg][0];

                         flg=min2[flg][1];

                         cnt=1;

                  }

                  else{

                         if(ansa+ansb+min1[flg][0]>x)break;

                         ansb+=(long long)min1[flg][0];

                         flg=min1[flg][1];

                         cnt=0;

                  }           

                  }

                  if(maxnb==0&&a[maxn]<a[i]){

                  maxna=ansa;maxnb=ansb;maxn=i;}//特殊情况处理//周全;

                  else if(ansb&&((double)maxna/maxnb>(double)ansa/ansb||((double)ansb*maxna==(double)maxnb*ansa&&a[maxn]<a[i]))){

                         maxna=ansa;maxnb=ansb;maxn=i;

                  }

           }

           printf("%d",maxn);

           scanf("%d",&m);

           for(int i=1;i<=m;i++)

           {

                  scanf("%d %d",&sta,&x);

                  long long flg=sta,cnt=0,ansa=0,ansb=0;//该改的还是要记得改啊;

                  while(flg<=2e9&&flg!=n){

                  if(!cnt){

                         if((long long)ansa+ansb+min2[flg][0]>(long long)x)break;

                         ansa+=(long long)min2[flg][0];

                         flg=min2[flg][1];cnt=1;

                  }

                  else{

                         if((long long)ansa+ansb+min1[flg][0]>(long long)x)break;

                         ansb+=(long long)min1[flg][0];

                         flg=min1[flg][1];cnt=0;

                  }           

                  }

                  printf(" %I64d %I64d",ansa,ansb);//输出变量;

           }

           return 0;

    }

  • 相关阅读:
    路飞学城Python-Day19
    路飞学城Python-Day18
    路飞学城Python-Day17
    ES6新特性概览
    关于Flex,有12个属性很重要
    Web前端工程师成长之路——知识汇总
    解决ajax跨域请求 (总结)
    项目中关于AJAX的使用总结
    Web前端性能优化——如何提高页面加载速度
    Canvas和SVG的区别
  • 原文地址:https://www.cnblogs.com/SindarDawn/p/5808483.html
Copyright © 2020-2023  润新知