1. 程式人生 > >PAT-ADVANCED1060——Are They Equal

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++解題報告: