题解 P1530 【分数化小数 Fractions to Decimals】
upd 2020/7/29 已修改代码中的错误
这是道有点复杂的模拟题
主要思路:长除
先得到整数部分存着暂时不管
处理N % D
主要思路:
如果长除中得到了一个之前出现过的结果 那么一定出现了循环节 所以把每一个余数出现的位置(初始值设为-1)都保存下来 如果发现一个余数的出现位置不是-1 那就找到了循环节 把循环节头尾记录下来
把小数部分每一位存下来 没有必要把不循环部分和循环部分分开来处理
模拟一下样例?
45 56 - 0
.
8 2 - 8
0 20 - 0
3 32 - 3
5 40 - 5
7 8 - 7
1 24 - 1
4 16 - 4
2 48 - 2
8 32 - 8
(5 40 - 5)
32出现过 571428为循环节
76个字符一换行 这个其实不算最恶心的输出格式了。。
存一个sum 先加上1(小数点) 加上整数部分位数(这个一定小于76 不用判)
然后 出一个字符sum++ sum只要加就判
详见代码
#include <bits/stdc++.h>
using namespace std;
int apr[100005] = {0}; //apr[i]表示余数i第一次出现的位置
int zs;
int xs[100005] = {0}; //xs[i]代表除下来结果小数部分第i位(从0开始)
//循环节最长为D-1 所以开100000+5就够了
int ans = 0,tmp,flag = 0;
int sum = 0; //sum记录目前已输出多少个字符
int main() {
int a,b,c,d;
scanf("%d %d",&a,&b);
memset(apr,-1,sizeof(apr));
//正文开始
zs = a / b; //整数部分
c = a % b; //拿余数继续处理
while(1) {
if(apr[c] != -1) { //如果该余数之前出现过 就代表循环节已经出现了 把ans变为循环节起始位置,tmp是终止位置
tmp = ans;
ans = apr[c];
break;
}
apr[c] = ans;
c = c * 10;
d = c / b;
c = c % b;
xs[ans] = d; //长除过程
ans ++; //ans表示当前正在处理第几位(从0开始)
if(c == 0) { //余数为零代表除尽 立个flag flag=1表示没有循环节
flag = 1;
break;
}
}
//正文结束,后面全是处理76个一换行
printf("%d.",zs);
sum ++;
while(1) {
sum ++;
zs = zs / 10;
if(zs == 0) break;
}
for(int i = 0; i < ans; i ++) {
printf("%d",xs[i]);
sum ++;
if(sum % 76 == 0) {
printf("\n");
}
}
if(flag == 0) { //存在循环节
printf("(");
sum ++;
if(sum % 76 == 0) {
printf("\n");
}
for(int i = ans; i < tmp; i ++) {
printf("%d",xs[i]);
sum ++;
if(sum % 76 == 0) {
printf("\n");
}
}
printf(")");
sum ++;
if(sum % 76 == 0) {
printf("\n");
}
}
return 0;
}