USACO 2.4.5《Fractions to Decimals》困扰了我很久,并不是题目本身有多难,而是之前的算法是一种不太自然的方式创建的,顺着算法的思路得到的答案总是若隐若现——有时候正确,有时候又会出现类似于错位的小错误。而我这个人又比较懒,不太愿意彻底去改变自认为已经考虑清楚的算法,而更多的是从小处着眼,增强程序的适应能力,这种改法往往治标不治本,最后的结果是让自己的思路变得很凌乱。
实验室原定今天下午的例会取消,我终于可以放下一直纠结于心的SOA,进入久违的USACO Training。首先入手就是上次没有解决的《Fractions to Decimals》,上次的程序调试了很长时间,已经通过了5组数据,在第6组数据的时候卡住了,错误有点莫名奇妙。我决定放弃原来的代码,重新整理思路。
写一个程序,输入一个形如N/D的分数(N是分子,D是分母),输出它的小数形式。如果小数有循环节的话,把循环节放在一对圆括号中。
例如, 1/3 =0.33333333 写成0.(3), 41/333 = 0.123123123... 写成0.(123), 用xxx.0 等表示整数。典型的转化例子:
1/3 = 0.(3)
22/5 = 4.4
1/7 = 0.(142857)
2/2 = 1.0
3/8 = 0.375
45/56 = 0.803(571428)
我在稿纸上整理几种可能的结果:
1. 整数。例如2/2【1.0】
2. 非循环小数。例如22/5【4.4】
3. 循环小数,循环节从小数点后第一位开始。例如1/7【0.(142857)】
4. 循环小数,循环节从小数点后第一位开始。例如45/56【0.803(571428)】
在稿纸上重新演算了45/56的整体过程。重新定义算法:
先读入并打印整书部分。接下来,对剩下的真分数部分进行长除直到我们发现了重复的余数或余数变为0。如果我们发现了重复的余数,即出现了循环节,就分别恰当地打印重复的部分和不重复的部分。如果余数变为0,即已经除尽,就打印整个小数部分。如果小数位根本没有生成,那么就打印一个0就是是正确答案了。
由于最后的输出有格式要求,我们从一开始将整数以及小数的余数部分存储起来,到最后统一输出。
/*
ID:lichaoy2
TASK:fracdec
LANG:C++
*/
#include <iostream>
#include <fstream>
#include <vector>
#include <cstring>
using namespace std;
#define MAXN 100000
int main()
{
int N,D,i,k,t;
int remainder[MAXN];
vector<int> digit;
//余数初始化
for (i=0; i<MAXN; ++i)
remainder[i] = -1;
ifstream fin("fracdec.in");
//输入
fin>> N >> D;
fin.close();
//找到小数点前的部分
t = N/D;
k= N%D;
remainder[0]=0;
//计算小数点后的部分
for (; ; )
{
if (remainder[k] >= 0)
break;
digit.push_back(k);
remainder[k] = k*10/D;
k = (k*10)%D;
}
ofstream fout("fracdec.out");
char str[MAXN];
sprintf(str,"%d",t);
fout << str << ".";
if (k==0)//如果是整除
{
if (digit.size() == 0)
fout << "0";
for (i=0; i<digit.size(); ++i)
{
if ((strlen(str)+1+i)%76==0)
fout << endl;
fout << remainder[digit[i]];
}
fout << endl;
}
else
{
for (i=0; digit[i] != k; ++i)
{
if ((strlen(str)+1+i)%76==0)
fout << endl;
fout << remainder[digit[i]];
}
//输出循环节
fout << "(";
for (;i<digit.size(); ++i)
{
if ((strlen(str)+2+i)%76==0)
fout << endl;
fout << remainder[digit[i]];
}
if ((strlen(str)+2+i)%76==0)
fout << endl;
fout << ")"<<endl;
}
fout.close();
return 0;
}
这题给我的启示是:遇到题目要以一种更加自然的方式去思考,找到最容易理解的解决方案,这样排错和调试都会更加方便。