我的PAT-ADVANCED代码仓:https://github.com/617076674/PAT-ADVANCED
原题链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805413520719872
题目描述:
题目翻译:
1060 它们是否相等
如果机器只能保存3位有效数字,则浮点数12300和12358.9被认为是相等的,因为它们都保存为0.123×10 ^ 5。现在给出一台机器上的有效位数和两个浮点数,你需要指出它们在该机器中是否被视为相等。
输入格式:
每个输入文件包含一个测试用例,它给出三个数字N,A和B,其中N(<100)是有效位数,A和B是要比较的两个浮点数。 每个浮点数都是非负数,不大于10 ^ 100,即总位数小于100。
输出格式:
对于每个测试用例,如果两个数字处理相等,则在一行中打印“Yes”,然后以标准形式编号为0.d [1] ... d [N] * 10 ^ k(d [1]> 0 除非数字是0);如果相等,则输出“NO”,然后是标准形式的两个数字。 所有数字由一个空格分隔,一行末尾没有多余空格。
注意:不考虑四舍五入。
输入样例1:
3 12300 12358.9
输出样例1:
YES 0.123*10^5
输入样例2:
3 120 128
输出样例2:
NO 0.120*10^3 0.128*10^3
知识点:字符串
先写几个坑点:
(1)题目的数字可能有前导0,即00123这种形式。
(2)题目中的数字可能出现0.000、1.000这种形式。
(3)即使指数是0,也要写出来。
(4)指数可以是负数,也就是说对于0.000012的情况,其结果不应该是0.000,而应该是0.120 * 10 ^ -4次。
题目的要求是将两个数改写为科学计数法的形式,然后判断它们是否相等。而科学计数法的写法一定是如下格式:0.a1a2a3... * 10 ^ e,因此只需要获取到科学计数法的本体部分a1a2a3与指数e,即可判定两个数在科学计数法形式下是否相等。
按整数部分是否为0来分情况讨论,即
(1)0.a1a2a3...
(2)b1b2...bm.a1a2a3...
现在来考虑这两种情况的本体部分与指数分别是什么(以下讨论均按有效位数为3进行)。对(1)来说,由于在小数点后面还可能跟着若干个0,因此本体部分是从小数点后第一个非零位开始的3位(即akak + 1ak + 2,其中ak是小数点后第一个非零位),而指数则是小数点与该非零位之间0的个数的相反数(例如0.001的指数为-2)。在分析清楚后,具体的代码实现逻辑也就成形了,即令指数e的初值为0,然后在小数点后每出现一个0,就让e减1,直到到达最后一位(因为有可能是小数点后全为0的情况)或是出现非零位为止。
然后来看(2)的情况,假设b1不为零。很显然,其本体部分就是从b1开始的3位,而指数则是小数点前的总位数m。具体实现中,则可以令指数e的初值为0,然后从前往后枚举,只要不到达最后一位(因为有可能没有小数点)或是出现小数点,就让e加1。
如何区分给定的数是(1)还是(2)呢?先去除所有的前导0,按去除前导0后的字符串的第一位是否是小数点来判断其属于(1)或是(2)。
由于需要让两个数的科学计数法进行比较,因此必须将各自的本体部分单独提取出来。比较合适的方法是,在按上面的步骤处理(1)时,将前导0、小数点、第一个非零位前的0全部删除,只保留第一个非零位开始的部分(即akak + 1ak + 2...)。在处理(2)时,将前导0、小数点删除,保留从b1开始的部分(即b1b2...bma1a2a3...)。这些删除操作可以在上面获取指数e的过程中同时做到(使用string的erase函数)。之后便可以对剩余的部分取其有效位数的部分赋值到新字符串中,长度不够有效位数则在后面补0。
最后只要比较本体部分和指数是否都相等,就可以决定输出“YES”或“NO”。
时间复杂度和空间复杂度的分析对本题来说意义不大。
C++代码:
#include
#include
using namespace std;
int n; //有效位数
string deal(string s, int &e);
int main(){
string s1, s2, s3, s4;
cin >> n >> s1 >> s2;
int e1 = 0, e2 = 0; //e1,e2为s1与s2的指数
s3 = deal(s1, e1);
s4 = deal(s2, e2);
if(s3 == s4 && e1 == e2){
cout << "YES 0." << s3 << "*10^" << e1 << endl;
}else{
cout << "NO 0." << s3 << "*10^" << e1 << " 0." << s4 << "*10^" << e2 << endl;
}
}
string deal(string s, int &e) {
int k = 0; //s的下标
while(s.length() > 0 && s[0] == '0') {
s.erase(s.begin()); //去掉s的前导零
}
if(s[0] == '.') { //去掉前导零后是小数点,说明s是小于1的小数
s.erase(s.begin()); //去掉小数点
while(s.length() > 0 && s[0] == '0') {
s.erase(s.begin()); //去掉小数点后非零位前的所有零
e--; //每去掉一个0,指数e减1
}
} else { //去掉前导零后不是小数点,则找到后面的小数点删除
while(k < s.length() && s[k] != '.') { //寻找小数点
k++;
e++; //只要不碰到小数点就让指数e++
}
if(k < s.length()) {
s.erase(s.begin() + k); //把小数点删除
}
}
if(s.length() == 0) { //如果去除前导零后s的长度变为0,说明这个数是0
e = 0;
}
int num = 0;
k = 0;
string res;
while(num < n){ //只要精度还没有到n
if(k < s.length()){
res += s[k++];
}else{
res += '0';
}
num++;
}
return res;
}
C++解题报告: