题目地址传送门:URAL 1547
这道题需要用到大数的很多模板,推荐大家去刷刷!
题目大意:Vova忘记了在Timus OJ上面的密码了,密码是由小写字母(a~z)组成的,他只知道密码长度不大于n位,现在他需要用m台数据处理器对密码进行检索,其中检索顺序需要满足字典序。比如他的密码长度不大于2,那就需要依次检索a,b,..........,y,z,aa,ab,..........,zy,zz.输出每台数据检索器的检索区间,使得总的检索效率可以达到最高。
已知密码的总可能数不少于数据处理器个数。
对于这个题目,刚开始是有点懵的。以为只用一次的26进制就可以表示所有的数(a到zzzzz..........z),后来发现是不行的
接下来发现还有这种密码还有这样一个特性:
当密码长度为一位(a,b,..........,z):数量为26,即密码长度不大于1总数为26
长度为二位(aa,ab,..........,zz):数量为26*26,即密码长度不大于2总数为26+26^2
..........
长度为n位的数量为:26^n,即密码长度不大于n总数为26+26^2+..........+26^n
为使每个数据处理器效率达到最大,需要求出每段区间的数,这里需要用到大数除法求出余数和商,从而确定每个数据处理器的区间个数和起止的区间号
将区间号转换为对应密码长度位数的对应字符串(将十进制转换为二十六进制),输出对应的结果。
这道题需要用到很多个大数模板,于是自己就写了一点,预备着用
1.比较字符串的大小(不含0前缀,且字符串里的元素只有‘0’~‘9’构成
/* s1大于s2,返回1 s1小于s2,返回-1 s1等于s2,返回0 */ int cmp(char *s1,char *s2) { int len1=strlen(s1),len2=strlen(s2),i; if(len1>len2)return 1; else if(len1<len2)return -1; for(i=0;i<len1;i++) if(s1[i]>s2[i])return 1; else if(s1[i]<s2[i])return -1; return 0; }
2.大数相乘,这里只呈现整形和字符串相乘的结果
/* 执行函数后字符串s2的结果为字符串s1和整形x的结果(x>=0) */ void multi(char *s2,char *s1,int x) { int a[330],len1,i,cnt=0; memset(a,0,sizeof(a)); len1=strlen(s1); for(i=0;i<len1;i++)a[i]=s1[len1-i-1]-'0'; for(i=0;i<len1;i++)a[i]*=x; for(i=0;i<len1+5;i++) { a[i+1]+=a[i]/10; a[i]%=10; } for(i=len1+5;i>0;i--) //如果乘出来的结果可能为0,那就不能在for循环里将i置为i>=0 if(a[i])break; strcpy(s2,""); for(;i>=0;i--)s2[cnt++]=a[i]+'0'; s2[cnt]='\0'; }3.大数求和
/* 执行函数后字符串s3返回的是字符串表示的大数s1和s2的和 */ void add(char *s3,char *s2,char *s1) { int a1[330],a2[330],len1=strlen(s1),len2=strlen(s2),len,i,cnt=0; memset(a1,0,sizeof(a1)); memset(a2,0,sizeof(a2)); len=(len1>len2?len1:len2); for(i=0;i<len1;i++)a1[i]=s1[len1-i-1]-'0'; for(i=0;i<len2;i++)a2[i]=s2[len2-i-1]-'0'; for(i=0;i<len+2;i++)a1[i]=a1[i]+a2[i]; for(i=0;i<len+2;i++) { a1[i+1]+=a1[i]/10; a1[i]%=10; } for(i=len+2;i>0;i--) //如果考虑到相加结果可能为0,那就不能在for循环条件中将i置为i>=0,其他条件一般可以保留 if(a1[i])break; strcpy(s3,""); for(;i>=0;i--)s3[cnt++]=a1[i]+'0'; s3[cnt]='\0'; }
4.大数自加
/* 返回大数s1自加1后的效果 */ void addpp(char *s1) { int i,len=strlen(s1),a[330],cnt=0; memset(a,0,sizeof(a)); for(i=0;i<len;i++) a[i]=s1[len-1-i]-'0'; a[0]++; for(i=0;i<len;i++) { a[i+1]+=a[i]/10; a[i]=a[i]%10; } for(i=len+2;i>=0;i--)if(a[i])break; for(;i>=0;i--)s1[cnt++]=a[i]+'0'; s1[cnt]='\0'; }5.大数减法
/* 执行函数后s3表示s2减去s1的结果,其中s2大于等于s1 */ void minus(char *s3,char *s2,char *s1) { int a1[330],a2[330],i,len1=strlen(s1),len2=strlen(s2),cnt=0; memset(a1,0,sizeof(a1)); memset(a2,0,sizeof(a2)); for(i=0;i<len1;i++)a1[i]=s1[len1-i-1]-'0'; for(i=0;i<len2;i++)a2[i]=s2[len2-i-1]-'0'; for(i=0;i<len2;i++) { if(a1[i]>a2[i]) { a2[i+1]--; a1[i]=a2[i]+10-a1[i]; } else { a1[i]=a2[i]-a1[i]; } } for(i=len2;i>0;i--) //warning if(a1[i])break; for(;i>=0;i--)s3[cnt++]=a1[i]+'0'; s3[cnt]='\0'; }6.大数自减
/*执行函数后s1返回s1自减1后的结果,其中s1大于0 void minuspp(char *s1) { int i,len=strlen(s1),a[330],cnt=0; memset(a,0,sizeof(a)); for(i=0;i<len;i++) a[i]=s1[len-1-i]-'0'; a[0]--; for(i=0;i<len;i++) { if(a[i]==-1) { a[i+1]--; a[i]=9; } else break; } for(i=len+2;i>0;i--) //warning if(a[i])break; for(;i>=0;i--)s1[cnt++]=a[i]+'0'; s1[cnt]='\0'; }7.大数除法(字符串类型除整形)
/* 这里num表示全局变量,返回的是s1mod(mm)的值,即余数,s2返回的是s1/mm的值 */ int num; void divi(char *s2,char *s1,int mm) { char s3[330]; int len=strlen(s1),i,cnt=0; num=0; strcpy(s3,""); for(i=0;i<len;i++) { num=num*10+s1[i]-'0'; s3[cnt++]=num/mm+'0'; num=num%mm; } s3[cnt]='\0'; for(i=0;i<len-1;i++) //当考虑到s1可能会小于mm时,在for循环中需要将i置为i<len-1 if(s3[i]!='0')break; cnt=0; for(;i<len;i++) s2[cnt++]=s3[i]; s2[cnt]='\0'; }模板就先整理这几个吧,1547这道题都用上了,或许也还有表述不准确的地方,也希望大家多多指出!
timus 1547. Password Search【题意思路+大数模板】
原文地址:http://blog.csdn.net/quanqiuzhongzi/article/details/42586813