前言
东莞市小学生镇赛一道题。。。
描述
题目大意
给定A和B,求出A/B的循环节及混循环部分
注:混循环部分,即除循环节外的部分,例如
34/35=0.9(714285)
9为混循环部分,714285为循环部分,即循环节
若A/B为正整数,则输出A/B.0,例如2/1=2.0
思路
两种方法都是建立在模拟高精除运算的基础上
方法一,通过商来求循环节
先利用数学公式求出混循环部分的长度,然后算出所有的商,再枚举循环节的长度,判断即可AC
时间复杂度:
方法二,通过余数来求循环节
方法二较方法一更为优秀,我们只需判断当前余数是否出现,然后判断一下,输出即可,详见代码
时间复杂度:
代码
方法一
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<cmath>
using namespace std;int n,m,d,k,len=0,a[501],j,b[1001],bj,len2;
inline int gcd(register int x,register int y){return y?gcd(y,x%y):x;}//辗转相除法
signed main()
{
scanf("%d%d",&n,&m);
printf("%d/%d=",n,m);
if(n%m==0) {printf("%d.0",n/m);return 0;}
d=gcd(n,m);
n/=d;m/=d;//约分
int B=m;
while(1)//以下为计算混循环长度的方法,即混循环部分的长度为除数中2和5的倍数之和
{
d=gcd(B,10);
if(d==1) break;B/=d;
len++;//计算其混循环部分的长度
}
k=m;
while(k!=1)//这是另一种求法,方法跟上面是一样的,这里打出来是为了方便理解
{
if(!(k%5)) k/=5;//能被5整除就除以5
else if(!(k&1)) k>>=1;//能被2整除就除以2
else break;//两个都除不了
}
if(k==1) cout<<fixed<<setprecision(len)<<n/1.0/m;//计算出小数位数长度,输出
else
{
printf("%d.",n/m);//首先计算出整数部分
int g=n%m;//计算出整数部分的余数
int beg=(int)log(n)+1;//beg为除法开始运算的位置
while(n) a[++j]=n%10,n/=10;//转换成数组
reverse(a+1,a+1+j);//翻转
for(int i=beg+1,t=0;i<=500;i++)//模拟高精度除法
{
g=(g<<3)+(g<<1)+a[i];//余数*10+a[i]
b[i]=g/m;//计算b
g%=m;//计算余数
if(++t<=len) putchar(b[i]+48);//若其为混循环部分,输出
}
putchar('(');//以下为计算循环节
int i;
beg+=len;//beg要加上len因为我们已经计算过了混循环部分
for(i=1;i<=250;i++)
{
bool ok=true;
for(register int j=beg+1;j<=500-i;j++) if(b[j]!=b[j+i])
{ok=false;break;}
if(ok) break;
}
for(register int j=beg+1;j<=beg+i;j++) putchar(b[j]+48);//输出循环节部分
putchar(')');//记得输出括号
}
}
方法二
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<cmath>
using namespace std;int n,m,d,k,len=0,a[501],j,b[1001],bj;
inline int gcd(register int x,register int y){return y?gcd(y,x%y):x;}
signed main()
{
scanf("%d%d",&n,&m);
printf("%d/%d=",n,m);
if(n%m==0) {printf("%d.0",n/m);return 0;}
d=gcd(n,m);
n/=d;m/=d;//约分
int B=m;
while(1)//以下为计算混循环长度的方法,即混循环部分的长度为除数中2和5的倍数之和
{
d=gcd(B,10);
if(d==1) break;B/=d;
len++;//计算其混循环部分的长度
}
k=m;
while(k!=1)//这是另一种求法,方法跟上面是一样的,这里打出来是为了方便理解
{
if(!(k%5)) k/=5;//能被5整除就除以5
else if(!(k&1)) k>>=1;//能被2整除就除以2
else break;//两个都除不了
}
if(k==1) cout<<fixed<<setprecision(len)<<n/1.0/m;//计算出小数位数长度,输出
else
{
printf("%d.",n/m);//首先计算出整数部分
int g=n%m;//计算出整数部分的余数
int beg=(int)log(n)+1;//beg为除法开始运算的位置
while(n) a[++j]=n%10,n/=10;//转换成数组
reverse(a+1,a+1+j);//翻转
len++;//因为这是用余数去算的,位数要偏后一位,因为我们要晚一步计算
for(int i=beg+1,t=0;i<=500;i++)
{
if(++t==len) putchar('(');//混循环结束,循环节开始
g=(g<<3)+(g<<1)+a[i];//余数*10+a[i]
b[i]=g/m;
g%=m;
if(bj==g&&t>len){putchar(')');break;}//该余数已经出现过了,结束
putchar(b[i]+48);//输出
if(t==len) bj=g;//标记余数
}
}
}