機試編程(四)——數學問題(一)

機試編程(四)——數學問題(一)

目錄:

  1. 基礎知識
    1. 進制轉換
    2. 最大公約數和最小公倍數
    3. 分解質因數
    4. 質數(素數)
  2. 應用實例
    1. 二進制數【北京郵電大學】
    2. 進制轉換【清華大學】
    3. 10進制VS2進制【清華大學】
    4. 進制轉換2【清華大學】
    5. 八進制【華中科技大學】
    6. 又一版A+B【浙江大學】
    7. 進制轉換【北京大學】
    8. 數制轉換【北京大學】
    9. 最大公約數【計算機歷年考研複試上機題】
    10. 最小公倍數【華爲機試在線訓練】
    11. 最簡真分數【北京大學】
    12. 素數判定【哈爾濱工業大學】
    13. 素數【北京航天航空大學】
    14. Prime Number【上海交通大學】
    15. 整除問題【上海交通大學】

一、基礎知識

1、進制轉換

  • 進制由基數和位權組成。
  • 基數:指進制採用的數碼,基數爲n,就稱爲n進制。
  • 位權:進制中每個固定位置對應的值。
  • 值 = SUM(各數位的值乘以其對應的位權)。

2、最大公約數和最小公倍數

  • 最大公約數(Greatest Common Divisor,GCD)
    • 定義:兩個或多個整數共有約數中,最大的一個約數。
    • 常用方法:歐幾里得算法(輾轉相除法)
      • a = g * m,b = g * n
      • 又a = b * k + r,其中r爲a除以b後的餘數
      • 則g * m = g * n * k + r,即r = g * (m - n - k)
      • a、b的公約數g可以整除a除以b後的餘數r(a mob b),即a、b的公約數和(b, a mod b)是一樣的,其最大公約數也必然相等。
  • 最小公倍數(Least Common Multiple,LCM)
    • 定義:兩個多多個整數公有的倍數中,除0以外最小的那一個公倍數。
    • a、b兩個數的最小公倍數爲兩數的乘積除以它們的最大公約數。

3、分解質因數

  • 定義:每個數都可以寫成一個或幾個質數相乘的形式,其中每個質數都是這個數的質因數。把一個數用質因數相乘的形式表示出來,就稱爲分解質因數。

4、質數(素數)

  • 定義:只能被其自身和1整除的正整數。
  • 常用方法:用所有小於等於sqrt(n)的正整數去試着整除該數,若存在某個數能夠整除該數,則該數不是素數;若這些數都不能整除它,則該數爲素數。

二、應用實例

1、題目描述:大家都知道,數據在計算機裏中存儲是以二進制的形式存儲的。 有一天,小明學了C語言之後,他想知道一個類型爲unsigned int 類型的數字,存儲在計算機中的二進制串是什麼樣子的。 你能幫幫小明嗎?並且,小明不想要二進制串中前面的沒有意義的0串,即要去掉前導0。【北京郵電大學】

  • 輸入格式:每行有一個數字n(0<=n<=10^8),表示要求的二進制串。
  • 輸出格式:輸出共T行。每行輸出求得的二進制串。
  • 樣例輸入:
    • 23
    • 535
    • 2624
    • 56275
    • 989835
  • 樣例輸出:
    • 10111
    • 1000010111
    • 101001000000
    • 1101101111010011
    • 11110001101010001011

示例代碼:

#include <iostream>
#include <stack>

using namespace std;

stack<int> myStack;

void GetBinaryStr(int num){
	while(num != 0){
		myStack.push(num % 2);
		num /= 2;
	}
}

int main(){
	int inputNumber;
	while(cin >> inputNumber){
		GetBinaryStr(inputNumber);
		while(!myStack.empty()){
			cout << myStack.top();
			myStack.pop();
		}
		cout << endl;
	}
	return 0;
}

2、題目描述:將一個長度最多爲30位數字的十進制非負整數轉換爲二進制數輸出。【清華大學】

  • 輸入格式:多組數據,每行爲一個長度不超過30位的十進制非負整數。(注意是10進制數字的個數可能有30個,而非30bits的整數)
  • 輸出格式:每行輸出對應的二進制數。
  • 樣例輸入:
    • 0
    • 1
    • 3
    • 8
  • 樣例輸出:
    • 0
    • 1
    • 11
    • 1000

示例代碼:

#include <iostream>
#include <stack>
#include <string>

using namespace std;

string myString;
stack<int> myStack;

string Divide(string s, int x){
	int remainder = 0;
	for(int i = 0; i < myString.size(); i++){
		int current = remainder * 10 + s[i] - '0';
		s[i] = current / 2 + '0';
		remainder = current % x;
	}
	int pos = 0;
	while(s[pos] == '0'){
		pos++;
	}
	return s.substr(pos);
}

int main(){
	while(cin >> myString){
		while(myString.size() != 0){
			int last = myString[myString.size() - 1] - '0';
			myStack.push(last % 2);
			myString = Divide(myString, 2);
		}
		while(!myStack.empty()){
			cout << myStack.top();
			myStack.pop();
		}
		cout << endl;
	}
	return 0;
}

附註:

(1)字符串除法:從高位到低位逐位除以除數,如果某位不能整除,那麼就保留它除以除數的餘數,餘數乘以10後和低一位一起進行處理。

3、題目描述:對於一個十進制數A,將A轉換爲二進制數,然後按位逆序排列,再轉換爲十進制數B,我們稱B爲A的二進制逆序數。     例如對於十進制數173,它的二進制形式爲10101101,逆序排列得到10110101,其十進制數爲181,181即爲173的二進制逆序數。【清華大學】

  • 輸入格式:一個1000位(即10^999)以內的十進制數。
  • 輸出格式:輸入的十進制數的二進制逆序數。
  • 樣例輸入:
    • 173
  • 樣例輸出:
    • 181

示例代碼:

#include <iostream>
#include <string>
#include <queue>
#include <cmath>

using namespace std;

string inputStr;
queue<int> myQueue;

//字符串除法
string Divide(string s, int x){
	int remainder = 0;
	for(int i = 0; i < s.size(); i++){
		int current = remainder * 10 + s[i] - '0';
		s[i] = current / 2 + '0';
		remainder = current % x;
	}
	int pos = 0;
	while(s[pos] == '0'){
		pos++;
	}
	return s.substr(pos);
}

//字符串乘法
string Multiple(string s, int x){
	int carry = 0;
	for(int i = s.size() - 1; i >= 0; i--){
		int current = x * (s[i] - '0') + carry;
		s[i] = current % 10 + '0';
		carry = current / 10;
	}
	if(carry != 0){
		s = "1" + s;
	}
	return s;
}

//字符串加法
string Add(string s, int x){
	int carry = x;
	for(int i = s.size() - 1; i >= 0; i--){
		int current = (s[i] - '0') + carry;
		s[i] = current % 10 + '0';
		carry = current / 10;
		if(carry == 0){
			break;
		}
	}
	if(carry != 0){
		s = "1" + s;
	}
	return s;
}

int main(){
	while(cin >> inputStr){
		while(inputStr.size() != 0){
			int last = inputStr[inputStr.size() - 1] - '0';
			myQueue.push(last % 2);
			inputStr = Divide(inputStr, 2);
		}
		string answer = "0";
		while(!myQueue.empty()){
			answer = Multiple(answer, 2);
			answer = Add(answer, myQueue.front());
			myQueue.pop();
		}
		cout << answer << endl;
	}
	return 0;
}

附註:

(1)二進制轉換爲十進制:從最高位開始,逐次乘2,然後加上該位,同時對進位進行處理。

4、題目描述:將M進制的數X轉換爲N進制的數輸出。【清華大學】

  • 輸入格式:輸入的第一行包括兩個整數:M和N(2<=M,N<=36)。下面的一行輸入一個數X,X是M進制的數,現在要求你將M進制的數X轉換成N進制的數輸出。
  • 輸出格式:輸出X的N進製表示的數。
  • 樣例輸入:
    • 10 2
    • 11
  • 樣例輸出:
    • 1011

示例代碼:

#include <iostream>
#include <string>
#include <stack>

using namespace std;

int CharToInt(char c){
	if(c >= '0' && c <= '9'){
		return c - '0';
	}else if(c >= 'a' && c <= 'z'){
		return c - 'a' + 10;
	}else{
		return c - 'A' + 10;
	}
}

char IntToChar(int x){
	if(x >= 0 && x <= 9){
		return x + '0';
	}else {
		return x + 'a' - 10;
	}
}

int main(){
	int m, n;
	string inputStr;
	cin >> m >> n;
	cin >> inputStr;
	long long num = 0;
	for(int i = 0; i < inputStr.size(); i++){ //m進制轉換爲10進制
		num *= m;
		num += CharToInt(inputStr[i]);
	}
	stack<char> result;
	while(num != 0){//十進制轉換爲n進制
		result.push(IntToChar(num % n));
		num /= n;
	}
	while(!result.empty()){
		cout << result.top();
		result.pop();
	}
	cout << endl;
	return 0;
}

5、題目描述:輸入一個整數,將其轉換成八進制數輸出。【華中科技大學】

  • 輸入格式:輸入包括一個整數N(0<=N<=100000)。
  • 輸出格式:可能有多組測試數據,對於每組數據,輸出N的八進制表示數。
  • 樣例輸入:
    • 7
    • 8
    • 9
  • 樣例輸出:
    • 7
    • 10
    • 11

示例代碼:

#include <iostream>
#include <vector>

using namespace std;

vector<int> myVector;

void Divide(int n){
	int tmp;
	while(n != 0){
		tmp = n % 8;
		myVector.push_back(tmp);
		n /= 8;
	}
}

int main(){
	int n;
	while(cin >> n){
		Divide(n);
		for(int i = myVector.size() - 1; i >= 0; i--){
			cout << myVector[i];
		}
		cout << endl;
		myVector.clear();
	}
	return 0;
}

6、題目描述:輸入兩個不超過整型定義的非負10進制整數A和B(<=231-1),輸出A+B的m (1 < m <10)進制數。【浙江大學】

  • 輸入格式:輸入格式:測試輸入包含若干測試用例。每個測試用例佔一行,給出m和A,B的值。當m爲0時輸入結束。
  • 輸出格式:輸出格式:每個測試用例的輸出佔一行,輸出A+B的m進制數。
  • 樣例輸入:
    • 8 1300 48
    • 2 1 7
    • 0
  • 樣例輸出:
    • 2504
    • 1000

示例代碼:

#include <iostream>
#include <vector>

using namespace std;

vector<int> myVector;

void Divide(long long n, int m){
	int tmp;
	while(n != 0){
		tmp = n % m;
		myVector.push_back(tmp);
		n /= m;
	}
}

int main(){
	int m, a, b;
	while(cin >> m && m != 0 && cin >> a >> b){
		if(a + b == 0){
			cout << 0 << endl;
		}else{
			Divide(a + b, m);
			for(int i = myVector.size() - 1; i >= 0; i--){
				cout << myVector[i];
			}
			cout << endl;
			myVector.clear();
		}
	}
	return 0;
}

7、題目描述:寫出一個程序,接受一個十六進制的數值字符串,輸出該數值的十進制字符串(注意可能存在的一個測試用例裏的多組數據)。【北京大學】

  • 輸入格式:輸入一個十六進制的數值字符串。
  • 輸出格式:輸出該數值的十進制字符串。
  • 樣例輸入:
    • 0xA
  • 樣例輸出:
    • 10

示例代碼1:

#include <iostream>
#include <string>

using namespace std;

int CharToInt(char c){
	if(c >= '0' && c <= '9'){
		return c - '0';
	}else if(c >= 'a' && c <= 'z'){
		return c - 'a' + 10;
	}else{
		return c - 'A' + 10;
	}
}

int main(){
	string inputStr;
	while(cin >> inputStr){
		long long num = 0;
		for(int i = 2; i < inputStr.size(); i++){ 
			num *= 16;
			num += CharToInt(inputStr[i]);
		}
		cout << num << endl;
	}
	return 0;
}

示例代碼2:

#include <iostream>

using namespace std;

int main(){
	long long inputNumber;
	while(cin >> hex >> inputNumber){
		cout << dec << inputNumber << endl;
	}
	return 0;
}

8、題目描述:求任意兩個不同進制非負整數的轉換(2進制~16進制),所給整數在long所能表達的範圍之內。    不同進制的表示符號爲(0,1,...,9,a,b,...,f)或者(0,1,...,9,A,B,...,F)。【北京大學】

  • 輸入格式:輸入只有一行,包含三個整數a,n,b。a表示其後的n 是a進制整數,b表示欲將a進制整數n轉換成b進制整數。a,b是十進制整數,2 =< a,b <= 16。數據可能存在包含前導零的情況。
  • 輸出格式:可能有多組測試數據,對於每組數據,輸出包含一行,該行有一個整數爲轉換後的b進制數。輸出時字母符號全部用大寫表示,即(0,1,...,9,A,B,...,F)。
  • 樣例輸入:
    • 15 Aab3 7
  • 樣例輸出:
    • 210306

示例代碼:

#include <iostream>
#include <string>
#include <stack>

using namespace std;

int CharToInt(char c){
	if(c >= 'a' && c <= 'z'){
		return c - 'a' + 10;
	}else if(c >= 'A' && c <= 'Z'){
		return c - 'A' + 10;
	}else{
		return c - '0';
	}
}

char IntToChar(int i){
	if(i >= 0 && i <= 9){
		return i + '0';
	}else{
		return i + 'A' - 10;
	}
}

int main(){
	int from, to;
	string str;
	while(cin >> from >> str >> to){
		int loc = 0;
		while(str[loc] == '0' && loc <= str.size() - 1){
			loc++;
		}
		if(loc == str.size()){
			cout << "0" << endl;
			continue;
		}
		str = str.substr(loc);
		//從from進制到十進制
		long long num = 0;
		for(int i = 0; i < str.size(); i++){
			num *= from;
			num += CharToInt(str[i]);
		}
		//從十進制到to進制
		stack<char> myStack;
		while(num != 0){
			myStack.push(IntToChar(num % to));
			num /= to;
		}
		while(!myStack.empty()){
			cout << myStack.top();
			myStack.pop();
		}
		cout << endl;
	}
	return 0;
}

9、題目描述:讀入n個正整數,求出這n個數的最小值、最大值以及它們兩的最大公約數,並輸出。輸入中第一行爲n,接下來爲n個大於零的整數。【計算機歷年考研複試上機題】

  • 輸入格式:第一行爲n。第二行是n個大於零的整數,用空格隔開。
  • 輸出格式:分別輸出最小值、最大值和它們兩的最大公約數,用空格隔開。
  • 樣例輸入:
    • 3
    • 4 8 6
  • 樣例輸出:
    • 4 8 4

示例代碼:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

vector<int> myVector;

int GCD(int a, int b){
	if(b == 0){
		return a;
	}else{
		return GCD(b, a % b);
	}
}

int main(){
	int n, inputNumber;
	while(cin >> n){
		for(int i = 0; i < n; i++){
			cin >> inputNumber;
			myVector.push_back(inputNumber);
		}
		sort(myVector.begin(), myVector.end());
		cout << myVector.front() << " " << myVector.back() << " ";
		cout << GCD(myVector.front(), myVector.back()) << endl;
		myVector.clear();
	}
	return 0;
}

10、題目描述:正整數A和正整數B 的最小公倍數是指 能被A和B整除的最小的正整數值,設計一個算法,求輸入A和B的最小公倍數。【華爲機試在線訓練】

  • 輸入格式:輸入兩個正整數A和B。
  • 輸出格式:輸出A和B的最小公倍數。
  • 樣例輸入:
    • 5 7
  • 樣例輸出:
    • 35

示例代碼:

#include <iostream>

using namespace std;

int GCD(int a, int b){
	if(b == 0){
		return a;
	}else{
		return GCD(b, a % b);
	}
}

int main(){
	int a, b;
	while(cin >> a >> b){
		cout << a * b / GCD(a, b) << endl;
	}
	return 0;
}

11、給出n個正整數,任取兩個數分別作爲分子和分母組成最簡真分數,編程求共有幾個這樣的組合。【北京大學】

  • 輸入格式:每組包含n(n<=600)和n個不同的整數,整數大於1且小於等於1000。
  • 輸出格式:每行輸出最簡真分數組合的個數。
  • 樣例輸入:
    • 7
    • 3 5 7 9 11 13 15
    • 2 4 5
    • 0
  • 樣例輸出:
    • 17 
    • 2

示例代碼:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
vector<int> myVector;

int GCD(int a, int b){
	if(b == 0){
		return a;
	}else{
		return GCD(b, a % b);
	}
}

int main(){
	int n;
	while(cin >> n && n){
		int number;
		for(int i = 0; i < n; i++){
			cin >> number;
			myVector.push_back(number);
		}
		sort(myVector.begin(), myVector.end());
		int count = 0;
		for(int i = n - 1; i > 0; i--){
			for(int j = i - 1; j >= 0; j--){
				if(myVector[i] % myVector[j] != 0 && GCD(myVector[i], myVector[j]) == 1){
					++count;
				}
			}
		}
		cout << count << endl;
	}
	return 0;
}

附註:

(1)最簡真分數:分子小於分母,且分子和分母互質的分數。

12、題目描述:給定一個數n,要求判斷其是否爲素數(0,1,負數都是非素數)。【哈爾濱工業大學】

  • 輸入格式:測試數據有多組,每組輸入一個數n。
  • 輸出格式:對於每組輸入,若是素數則輸出yes,否則輸入no。
  • 樣例輸入:
    • 13
  • 樣例輸出:
    • yes

示例代碼:

#include <iostream>
#include <algorithm>

using namespace std;

bool IsPrimeNumber(int number){
	if(number <= 1){
		return false;
	}
	for(int i = 2; i <= sqrt(number); i++){
		if(number % i == 0){
			return false;
		}
	}
	return true;
}

int main(){
	int n;
	while(cin >> n){
		if(IsPrimeNumber(n)){
			cout << "yes" << endl;
		}else{
			cout << "no" << endl;
		}
	}
	return 0;
}

13、題目描述:輸入一個整數n(2<=n<=10000),要求輸出所有從1到這個整數之間(不包括1和這個整數)個位爲1的素數,如果沒有則輸出-1。【北京航天航空大學】

  • 輸入格式:輸入有多組數據。每組一行,輸入n。
  • 輸出格式:輸出所有從1到這個整數之間(不包括1和這個整數)個位爲1的素數(素數之間用空格隔開,最後一個素數後面沒有空格),如果沒有則輸出-1。
  • 樣例輸入:
    • 100
  • 樣例輸出:
    • 11 31 41 61 71

示例代碼:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

vector<int> myVector;

bool IsPrimeNumber(int number){
	for(int i = 2; i <= sqrt(number); i++){
		if(number % i == 0){
			return false;
		}
	}
	return true;
}

int main(){
	int n;
	while(cin >> n){
		for(int i = 2; i < n; i++){
			if(IsPrimeNumber(i) && i % 10 == 1){
				myVector.push_back(i);
			}
		}
		if(myVector.size() == 0){
			cout << "-1" << endl;
		}else{
			for(int i = 0; i < myVector.size() - 1; i++){
				cout << myVector[i] << " ";
			}
			cout << myVector[myVector.size() - 1] << endl;
		}
		myVector.clear();
	}
	return 0;
}

14、題目描述:Output the k-th prime number.【上海交通大學】

  • 輸入格式:k≤10000
  • 輸出格式:The k-th prime number.
  • 樣例輸入:
    • 3
    • 7
  • 樣例輸出:
    • 5
    • 17

示例代碼:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

vector<int> myVector;

bool IsPrimeNumber(int number){
	for(int i = 2; i <= sqrt(number); i++){
		if(number % i == 0){
			return false;
		}
	}
	return true;
}

int main(){
	int count = 10001;
	for(int i = 2; count != 0; i++){
		if(IsPrimeNumber(i)){
			myVector.push_back(i);
			count--;
		}
	}
	int k;
	while(cin >> k){
		cout << *(myVector.begin() + k - 1) << endl;
	}
	return 0;
}

15、題目描述:給定n,a求最大的k,使n!可以被a^k整除但不能被a^(k+1)整除。【上海交通大學】

  • 輸入格式:兩個整數n(2<=n<=1000),a(2<=a<=1000)
  • 輸出格式:一個整數
  • 樣例輸入:
    • 6 10
  • 樣例輸出:
    • 1

示例代碼:

#include <iostream>
#include <vector>
#include <algorithm>
#include <map>

using namespace std;

vector<int> primeVector;
map<int, int> aMap, nMap;

bool GetPrime(int n){
	for(int i = 2; i <= sqrt(n); i++){
		if(n % i == 0){
			return false;
		}
	}
	return true;
}

void DealMap(vector<int> primeVector, int j, map<int, int> &m){
	for(int i = 0; i < primeVector.size() && primeVector[i] <= j; i++){
		int factor = primeVector[i];
		while(j % primeVector[i] == 0){
			j /= primeVector[i];
			m[primeVector[i]]++;
		}
	}
}

int main(){
	int a, n;
	cin >> n >> a;
	int big = a > n ? a : n;
	for(int i = 2; i < big; i++){
		if(GetPrime(i)){
			primeVector.push_back(i);
		}
	}
	for(int j = n; j >= 2; j--){
		DealMap(primeVector, j, nMap);
	}
	DealMap(primeVector, a, aMap);
	int min = big;
	map<int, int>::iterator aIter = aMap.begin(), nIter = nMap.begin();
	while(aIter != aMap.end() && nIter != nMap.end()){
		while(aIter != aMap.end() && nIter != nMap.end() && aIter->first < nIter->first){
			aIter++;
		}
		while(aIter != aMap.end() && nIter != nMap.end() && aIter->first == nIter->first){
			int divide = aIter->second > nIter->second ? 
				aIter->second / nIter->second : nIter->second / aIter->second; 
			if(divide < min){
				min = divide;
			}
			aIter++;
			nIter++;
		}
		while(aIter != aMap.end() && nIter != nMap.end() && nIter->first < aIter->first){
			nIter++;
		}
	}
	if(min == big){
		cout << 0 << endl;
	}else{
		cout << min << endl;
	}
	return 0;
}

附註:

(1)比較n!和a的相同質因數的指數之比的最小值爲所求。
6!=2^4*3^2*5^1=720
10=2^1*5^1
有公共質因數2和5,其中質因數2的指數分別爲4和1(4:1 = 4),質因數5的指數分別爲1和1(1:1 = 1),所以答案爲1。

參考文獻:

[1]楊澤邦、趙霖. 計算機考研——機試指南(第2版). [M]北京:電子工業出版社,2019.11;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章