PAT-ADVANCED1060——Are They Equal
我的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
知識點:字串
思路:按整數部分是否為0來分情況討論
寫在前面:題目出得很爛,應該說,所有PAT上涉及這種數字格式轉換的題目沒有一題是說清楚題意的,PAT出題者的水平可見一斑。
先寫幾個坑點:
(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<iostream>
#include<string>
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++解題報告: