屑题。
题意
给出两个数,将它们写成保留N位小数的科学计数法后是否相等。如果相等,则输出YES,并给出该转换结果;如果不相等,则输出NO,并分别给出两个数的转换结果。
题目的要求是将两个数改写为科学计数法的形式,然后判断它们是否相等。而科学计数法的写法一定是如下格式:(0.a_1a_2a_3 cdots imes 10^e),因此只需要获取到科学计数法的本体部分(a_1a_2a_3)与指数(e),即可判定两个数在科学计数法形式下是否相等。
然后考虑数据本身,可以想到按整数部分是否为0来分情况讨论,即
- (0.a_1a_2a_3...)
- (b_1b_2 cdots b_m.a_1a_2a_3 cdots)
现在来考虑这两种情况的本体部分与指数分别是什么(以下讨论按有效位数为3进行)。
对①来说,由于在小数点后面还可能跟着若千个0,因此本体部分是从小数点后第一个非零位开始的3位(即(a_ka_{k+1}+a_{k+2}),其中(a_k)是小数点后第一个非零位),而指数则是小数点与该非
零位之间0的个数的相反数(例如0.001的指数为-2)。在分析清楚后,具体的代码实现逻辑也成形了,即令指数e的初值为0,然后在小数点后每出现一个0,就让e减1,直到到达最后一位(因为有可能是小数点后全为0的情况)或是出现非零位为止。
然后来看②的情况,假设(b_1)不为零。很显然,其本体部分就是从(b_1)开始的3位,而指数则是小数点前的数位的总位数m。具体实现中,则可以令指数e的初值为0,然后从前往后枚举,只要不到达最后一位(因为有可能没有小数点)或是出现小数点,就让e加1。
那么,如何区分给定的数是属于①还是②呢?事实上,题目隐含了一个trick:数据有可能出现前导0,即在①或者②的数据之前还会有若干个0 (例如000.01或是00123.45)。为了应对这种情况,需要在输入数据后的第一步就是去除所有前导0,这样就可以按去除前导零后的字符串的第一位是否是小数点来判断其属于①或是②。
由于需要让两个数的科学计数法进行比较,因此必须把各自的本体部分单独提取出来。比较合适的方法是,在按上面步骤处理①时,将前导零、小数点、第一个非零位前的0全部删除,只保留第一个非零位开始的部分(即(a_ka_{k+1}a_{k+2} cdots) );在处理②时,将前导零、小数点删除,保留从(b_1)开始的部分(即(b_1b_2cdots b_ma_1a_2a_3 cdots))。这些删除操作可以在上面获取指数e
的过程中同时做到(使用string 的 erase 函数)。之后便可以对剩余的部分取其有效位数的部分赋值到新字符串中,长度不够有效位数则在后面补0。
最后只要比较本体部分与指数是否都相等,就可以决定输出YES或NO。
以上为晴神思路,下面是我乱搞的(color{green}{AC})代码。
代码
string a,b;
int n;
int main()
{
cin>>n>>a>>b;
int apos=a.find('.'),bpos=b.find('.');
if(apos == -1) apos=a.size();
if(bpos == -1) bpos=b.size();
if(a.find('.') != string::npos)
a.erase(a.find('.'),1);
if(b.find('.') != string::npos)
b.erase(b.find('.'),1);
while(a[0] == '0') a.erase(a.begin()),apos--;
while(b[0] == '0') b.erase(b.begin()),bpos--;
while(a.size() < n) a=a+'0';
a="0."+a.substr(0,n);
while(b.size() < n) b=b+'0';
b="0."+b.substr(0,n);
if(a == b && apos == bpos || stof(a) == 0) cout<<"YES"<<' '<<a<<"*10^"<<apos<<endl;
else cout<<"NO"<<' '<<a<<"*10^"<<apos<<' '<<b<<"*10^"<<bpos<<endl;;
//system("pause");
return 0;
}