poj1019

来源:互联网 发布:黑客破解软件授权 编辑:程序博客网 时间:2024/05/22 07:03

这道题目不难但是很麻烦,而且题意容易误解。一开始我就没有完全搞懂题意,导致错误。最容易出错的地方在超过1位数的数字占用位数不再是1了,而是其数字个数,这也应该不算什么难点,只是容易搞成所有数字都是一位(一时就脑袋蒙了),若所有数字都是1位,那么就简单多了,直接用求和公式k*(1+k)/2,求出S1+S2+……+SK的所有数字个数,然后稍加处理即可。而这里不同数字要按照实际位数计算。不过分析思路还是同上。只是处理起来要麻烦多了。

分析如下:

可以二分查找出小于或等于n(即题目输入的i值)的k,即 S1+S2+……+SK<=n,具体计算方法稍后说明。这里有两种情况。

1)若为相等,那么第n位即为数字K的最后一位,直接输出

2)若小于,那么第n位必定为数字K+1序列数字中的某一位。

情形2)更复杂,也可以分两种情况:

i)当第n位数字为刚好1位数,2位数,……,k位数的结束位时,则必定为9

ii)否则,第n位数字为这些位中某个数的中间一位数字

而第ii)种情况又可以细分为如下两种情况:

iii 1)第n为数字为数字的末尾位,特殊处理

iii 2)否则,一般处理

这里详细介绍一下上面所述的关于如何计算S1+S2+……+Sk的问题。

这里设置两个数字first,和 num 。分别记录K位数的第一个数字和K位数的个数。例如1位数的第一个数字为1,个数为9,2位数第一个数字为10,个数为90,等等。

那么SK的计算方法为求出所有位数小于K数字位数的数字个数与它们各自位数的乘积之和(即位数小于K位数的所数字位数之,利用上面所述的两个数组),

然后加上(K-first[len(K)]+1)* len(K)。依次求出S1,S2,……SK的值,然后相加。

上述过程可以抽象为一个函数。

下面是代码:132K+94MS

#include <stdio.h>#include <stdlib.h> //初始化first与num数组int first[11]={0,1,10,100,1000,10000,100000,1000000,10000000,100000000};int   num[11]={0,9,90,900,9000,90000,900000,9000000,90000000,900000000};int Case;__int64 n; // 输入值i__int64 record; bool  trag; // 标记量__int64 Get_num(int number){ // S1+S2+S3+……SK求和__int64 pro=0;  // 保存结果for(int j=1;j<=number;j++){ // 循环求解SKint i,len=0,temp=j;while(temp>0){ // 求解数字位数len++;temp/=10;}for(i=1;i<len;i++) // 累加比该位数小的所有数字位数之和pro+=num[i]*i;pro+=(j-first[len]+1)*len; // 加上该位数的数字位数之和}return pro; //返回求和结果}int Binary_search(int left,int right){ // 二分查找Sk<=nwhile(left<=right){int mid=(left+right)>>1;if(Get_num(mid)<n)left=mid+1;else if(Get_num(mid)>n)right=mid-1;else{trag=1; //若相等return mid;}}trag=0; // 小于return right; // 注意此处为right而不是left,这样才能保证Sk<n}int main(){scanf("%d",&Case); //case数while(Case--){scanf("%I64d",&n);int Count=Binary_search(1,35000); //经过测试35000是比较合适的数字,保证二分查找的正确性if(trag){ // 若相等则直接输出最后一位数字printf("%d\n",Count%10);continue;} //否则判断是在末尾位还是中间位record=n-Get_num(Count);//printf("%I64d ====\n",record);int pivot=1;    while(true){if(record-num[pivot]*pivot>0){ //若还有多余的位数,则继续分解record-=num[pivot]*pivot;pivot++;}else if(record-num[pivot]*pivot<0){ //若没有多余的位数了,则设置标记为0,为中间位trag=0;break;}else{ // 若正好相等,则为结束位,设置标记为1trag=1;break;}}if(trag){ //若为结束位,直接输出9printf("9\n");    continue;}if(record%pivot==0){ // 判断若属于中间数字的最后一位,则特殊处理    int x=first[pivot]+record/pivot-1;printf("%d\n",x%10);continue;} //否则一般处理int x=first[pivot]+record/pivot;int index=pivot-record%pivot+1;pivot=1;while(x>0){if(pivot==index){printf("%d\n",x%10);break;}x/=10;pivot++;}}return 0;}


 也给出误解题意的程序(仅作参考)

#include <stdio.h>#include <stdlib.h>#include <math.h>int Case;__int64 n;bool trag;int Binary_search(int left,int right){while(left<=right){int mid=(left+right)>>1;if(n<(mid+1)*mid/2)right=mid-1;else if(n>(mid+1)*mid/2)left=mid+1;else{trag=1;return mid;}}trag=0;return right;}int main(){scanf("%d",&Case);while(Case--){scanf("%I64d",&n);int temp=Binary_search(1,2*int(sqrt(double(n)))+1);if(!trag)printf("%I64d\n",n-(1+temp)*temp/2);elseprintf("%d\n",temp);}return 0;}


 

 

0 0