题目 1875: 小数第n位

题目

我们知道,整数做除法时,有时得到有限小数,有时得到无限循环小数。
如果我们把有限小数的末尾加上无限多个0,它们就有了统一的形式。

本题的任务是:在上面的约定下,求整数除法小数点后的第n位开始的3位数。

输入
一行三个整数:a b n,用空格分开。a是被除数,b是除数,n是所求的小数后位置(0<a,b,n<1000000000)

输出
一行3位数字,表示:a除以b,小数后第n位开始的3位数字。

样例输入

1 8 1

样例输出

125

解题思路

暴力解。
(我开始写的很复杂,还想着如何判断循环小数。。。)

难点

关于运行时间的缩短是本题难点,只需用到求余和乘法的交换律即可,如((a*10%b)*10%b)*10%b…(n次)等价于(10n%b),从而减少了乘法的次数,可以在所需要取的小数点位数很大时,节约运行时间。

代码

看了大佬的解法,把每一次乘上的10改为1010,运行时间从1000+ms降到100+ms,满分代码如下:

#include<stdio.h>
#define ten 10000000000
int main()
{
	long int a,b,n,temp,k=1;
	scanf("%ld %ld %ld",&a,&b,&n);
	a%=b;
	while (k<(n-10)){
	    a = a*ten%b;
	    k+=10;
	}
	for (k;k<(n+3);k++)
	{
	    temp = (a*10)/b;
	    if (k>=n)
	        printf("%ld",temp);
	    a = (a*10)%b;
	}
	return 0;
}

错误代码

主要思路是枚举然后验证是否存在循环数组,判定的标准是个人随机设定的阈值:如果长度为j的数组重复出现10次,那么就判定为循环数组;这种人为设定的阈值科学性不够,在多次调整len和阈值threshold之后,得分大概在75左右:(最后附上了C语言网测试用例,供需要的小伙伴调试)

#include<bits/stdc++.h>
#define len 20000000
using namespace std;
char b[len];
vector <char> p;
int threshold = 10;//寻找是否有重复的最长长度
int min(int a, int b){
    return (a>b)?b:a;
}

long int atoi(char a[]){
    int i,lena = strlen(a);
    long int num = 0;
    for (i=0;i<lena;i++)
        num+=(a[i]-48)*pow(10,lena-1-i);
    return num;
}

void itoa(long int a){
    int k = 0;
    char t[len];
    while (a!=0){
        t[k++] = a%10+48;
        a/=10;
    }
    for (int i=0;i<k;i++)
        b[i] = t[k-1-i];
    b[k++] = '0';//末尾补0,为下一位除法算小数做准备
    b[k] = '\0';
}

int isrepeated(char a[], int lena){
    int j,i,k,y=1;//y代表是否找到重复
    for (j=threshold;j>=1;j--)
    {
        for(i=lena-1;i>(len-1-j);i--)//每j个一次循环
        {
            for (k=i;k>=(i-10*j);k-=j)//如果有10次重复出现就认为是重复的
            {
                if (a[k]!=a[k-j])
                {
                    y = 0;
                    break;
                }
            }
            if (y==0)//不需要再验证,当前的间隔不是循环的周期
                break;
        }
        if (y==1)//找到了
            return j;//返回循环的节律
    }
    return 0;
}

int main()
{
    int i,j,n,cs,num=0,k=0;//k记录小数点后位数数目
    int circle = 0;//循环小数
    long int temp;
	char a[len],cha='0';
	scanf("%s %d %d",a,&cs,&n);
	temp = atoi(a)%cs;//因为只需要小数部分,为了方便起见直接取余数
	itoa(temp);
	k = 1;//小数后第一位
	
	while (k!=min((n+3),len)){
	    a[k++] = atoi(b)/cs+48;
	    temp = atoi(b)%cs;
	    itoa(temp);
	    if (k>(len/10))
	    {
	        circle = isrepeated(a,k);
	        if (circle>0)
	        {
	            for (j=k-circle;j<k;j++)
	                p.push_back(a[j]);
	            break;
	        }
	    }
	}
	if (circle!=0)
	{
	    i = (n-k-circle)%circle;
	    j = (n+1-k-circle)%circle;
	    temp = (n+2-k-circle)%circle;
	    printf("%c%c%c",p[i],p[j],p[temp]);
	}
	else
	    printf("%c%c%c",a[n],a[n+1],a[n+2]);
	return 0;
}

测试用例
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值