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;

}

原文地址:https://www.cnblogs.com/SindarDawn/p/5808483.html