Acm队的流年对数学的研究不是很透彻,但是固执的他还是想一头扎进去。
浏览网页的流年忽然看到了网上有人用玫瑰花瓣拼成了521三个数字,顿时觉得好浪漫,因为每个男生都会不经意的成为浪漫的制造者。此后,流年走到哪里都能看到5、2、1三个数字,他怒了,现在他想知道在连续的数中有多少数全部包含了这三个数字。例如12356就算一个,而5111就不算。特别的,如果他看到了521三个数连续出现,会特别的愤怒。例如35210。
输入 多组测试数据: 一行给定两个数a,b(0<a,b<1000000),表示数字的开始和结束。 输出 一行显示他想要知道的数有几个及显示有多少个数字令他特别的愤怒。用空格隔开。 样例输入 200 500 300 900 1 600 样例输出 Case 1:2 0 Case 2:2 1 Case 3:6 1 来源 流年 上传者ACM_安鹏程
问题分析:
思路很简单,1.遍历范围内的数字。2.分别记录包含5,2,1的,包含相邻521的数的个数。
如何识别一个数字是否包含5,2,1 和包含相邻521,即分解数字的每一位,然后进行判断。
需要注意的是a,b(0<a,b<1000000),若再分解很容易使得超时。因此要用到预处理的思路,即实现求得所有范围内符合条件的数,进行记录。写了三次代码,参考了别人的代码。
第一次:
属于没有用预处理的情况
#include <iostream> #include <stdio.h> #include <string.h> #include <math.h> #include <vector> #include <queue> #include <stack> #include <map> #include <string> #include <algorithm> using namespace std; /* run this program using the console pauser or add your own getch, system("pause") or input loop */ int a,b; int main(int argc, char** argv) { while(scanf("%d%d",&a,&b) != EOF){ int count=1; int meetcount=0,angercount=0; for(int i=a;i<=b;i++){ //对于每一个数字,判断其是否满足条件 int f1=-1,f2=-1,f3=-1; //用于标记数字中是否有521,及其位置 int tmp=i; int index=1; //标记分解到哪一位 while(tmp!=0){ //截取tmp的一位 int c=tmp; tmp/=10; if(c == 5){ f1= index; } if(c == 2){ f2= index; } if(c == 1){ f3= index; } index++; } if(f1 != -1 && f2 != -1 && f3 !=-1){ //cout<<i<<endl; meetcount++; if(f1>f3 && 2*f2 == f1+f3){ angercount++; } } } cout<<"Case "<<count++<<":"<<meetcount<<" "<<angercount<<endl; } return 0; } 第二次:使用了预处理,但是处理的不够彻底,在预处理完后任然需要进行一次遍历累加。
#include <iostream> #include <stdio.h> #include <string.h> #include <math.h> #include <vector> #include <queue> #include <stack> #include <map> #include <string> #include <algorithm> #define MAX 1000001 using namespace std; /* a,b(0<a,b<1000000)若正常处理分解每一位,最坏时间复杂度为 1000000*1000000 因此一定会超时。 为防止超时,采用预处理的方法。预先求出0-1000000内所有数的结果。 */ int meetNum[MAX]; //若 meetNum[i]=1 表示i包含521 meetNum[i]=2 表示i包含连续的521 // meetNum[i]=0 表示i不包含521 void init() { for(int i=125;i<=MAX;i++){ int f1=-1,f2=-1,f3=-1; int tmp=i; int index=1; while(tmp!=0){ int c=tmp; tmp/=10; if(c == 1){ f1=index; } if(c == 2){ f2=index; } if(c == 5){ f3=index; } index++; } if(f1 != -1 && f2 != -1 && f3 != -1){ meetNum[i]=1; if(f3 > f1 && 2*f2 == f1+f3){ meetNum[i]=2; } } } } int main(int argc, char** argv) { init(); int a,b; int Casen=1; while(scanf("%d%d",&a,&b)!=EOF){ int ans1=0,ans2=0; for(int i=a;i<=b;i++){ if(meetNum[i] != 0){ ans1++; if(meetNum[i] == 2){ ans2++; } } } printf("Case %d:%d %d\n",Casen++,ans1,ans2); } return 0; }第三次: 预处理数据,直接记录了某个数字之前的满足条件的数字的个数,而不用再输出时再进行计算。代码:
#include <iostream> #include <stdio.h> #include <string.h> #include <math.h> #include <vector> #include <queue> #include <stack> #include <map> #include <string> #include <algorithm> #define MAX 1000001 using namespace std; /* a,b(0<a,b<1000000)若正常处理分解每一位,最坏时间复杂度为 1000000*1000000 因此一定会超时。 为防止超时,采用预处理的方法。预先求出0-1000000内所有数的结果。 */ int data1[MAX]; int data2[MAX]; void init() { memset(data1,0,sizeof(data1)); memset(data2,0,sizeof(data2)); for(int i=125;i<MAX;i++){ int f1=-1,f2=-1,f3=-1; int tmp=i; int index=1; while(tmp!=0){ int c=tmp; tmp/=10; if(c == 1){ f1=index; } if(c == 2){ f2=index; } if(c == 5){ f3=index; } index++; if(f2 == f1+1 && f3 == f2+1){ data2[i]=data2[i-1]+1; break; //有一种情况满足,则跳出 //cout<<i<<" "<<data2[i]<<endl; }else{ data2[i]=data2[i-1]; } } if(f1 != -1 && f2 != -1 && f3 != -1){ data1[i]=data1[i-1]+1; //cout<<i<<" "<<data1[i]<<endl; }else{ data1[i]=data1[i-1]; data2[i]=data2[i-1]; } } } int main(int argc, char** argv) { init(); int a,b; int Casen=1; while(scanf("%d%d",&a,&b)!=EOF){ printf("Case %d:%d %d\n",Casen++,data1[b]-data1[a-1],data2[b]-data2[a-1]); } return 0; }另外说明的是,第一次和第二次除了超时之外,在处理相邻521是否包含是也有问题,没能考虑到2521这种多次出现1,或2或5的情况。在第三次做了改正,即在遍历一个数字的每一位时就判断是否满足包含 相邻521的情况,一旦发现就直接结束判断过程,从而避免了2521这种情况产生的错误,具体看代码:
while(tmp!=0){ int c=tmp; tmp/=10; if(c == 1){ f1=index; } if(c == 2){ f2=index; } if(c == 5){ f3=index; } index++; if(f2 == f1+1 && f3 == f2+1){ data2[i]=data2[i-1]+1; break; //有一种情况满足,则跳出 //cout<<i<<" "<<data2[i]<<endl; }else{ data2[i]=data2[i-1]; } }
优秀代码:
01. #include<stdio.h> 02. int a[2][1000001]={0}; 03. int main() 04. { 05. int k=0,i,sum=0; 06. for(i=1;i<=1000000;i++) 07. { 08. if((i==5||(i/10)==5||(i/100)==5||(i/1000)==5||(i/10000)==5||(i/100000)==5)&&(i==2||(i/10)==2||(i/100)==2||(i/1000)==2||(i/10000)==2||(i/100000)==2)&&(i==1||(i/10)==1||(i/100)==1||(i/1000)==1||(i/10000)==1||(i/100000)==1)) 09. {sum++; 10. if(i/1000==521||(i/100)00==521||(i/10)00==521||i00==521)k++;} 11. a[0][i]+=sum; 12. a[1][i]+=k; 13. } 14. int m,n,w=0; 15. while(scanf("%d%d",&n,&m)!=EOF) 16. printf("Case %d:%d %d\n",++w,a[0][m]-a[0][n-1],a[1][m]-a[1][n-1]); 17. } 对比:优秀代码充分利用了输入只有1000000位,其中长长的if令人影响深刻,直接写出了5,2,1所有可能的位置。
之后判断是否相邻时也是暴力的直接在if里写出了所有可能的位置。 收获:
1.对于超时的处理方法可以考虑用预处理。
2.在一个数字中判断某几位是否包含时要注意单个数字的重复出现,如本题中2512的情况。
3.在写某个题的时候,针对题的特点进行处理,而不是只是用自己的背景知识。