【王道机试】第六章 数学问题

6.1 进制转换

例题6.1 二进制数

提交网址

#include <iostream>
#include <vector>

using namespace std;

int main(){
	unsigned int n;
	while(scanf("%d", &n) != EOF){
		vector<int> bin; 
		while(n > 0){
			bin.push_back(n % 2);
			n /= 2;			
		}
		int len = bin.size();
		for(int i=len-1; i>=0; i--){
			printf("%d", bin[i]);
		}
		printf("\n");	
	}
	return 0;
}

例题6.2 进制转换

输入的长度超过long long类型的长度,需要用字符串来存储。
实现进制转换的做法和日常在纸上实现的算法是一样的。
提交网址

#include <iostream>
#include <vector>
#include <string>

using namespace std;

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

int main(){
	string str;
	while(cin >> str){
		vector<int> binary;
		while(str.size() != 0){
			int last = str[str.size() - 1] - '0';
			binary.push_back(last % 2);
			str = Divide(str, 2);
		}
		for(int i=binary.size() - 1; i>=0; i--){
			printf("%d", binary[i]);
		}
		printf("\n");
	}
	return 0;
}

例题6.3 十进制与二进制

题目网址

#include <iostream>
#include <string>
#include <vector>

using namespace std;

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

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

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

int main(){
	string str;
	while(cin >> str){
		vector<int> binary;
		while(str.size() != 0){
			int last = str[str.size() - 1] - '0';
			binary.push_back(last % 2);
			str = Divide(str, 2);
		}
		string answer = "0";
		for(int i=0; i<binary.size(); i++){
			answer = Multiple(answer, 2);
			answer = Add(answer, binary[i]);
		}
		cout << answer << endl;
	}
	return 0;
}

例题6.4 进制转换2

提交网址

#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main(){
	int M, N;
	long long num = 0;
	string X;
	vector<char> out;
	cin >> M >> N >> X;
	for(int i=0; i<X.size(); i++){
		num *= M;
		if(X[i] >= '0' && X[i] <= '9'){
			num += X[i] - '0';
		}else if(X[i] >= 'A' && X[i] <= 'Z'){
			num += X[i] - 'A' + 10;
		} 
	}
	while(num > 0){
		int tmp = num % N;
		if(tmp >= 10){
			out.push_back(tmp - 10 + 'a');
		}else{
			out.push_back(tmp + '0');
		}
		num /= N;
	}
	for(int i=out.size()-1; i>=0; i--){
		cout << out[i];
	}
	cout << endl;
	return 0;
}

习题6.1 八进制

提交网址

#include <iostream>
#include <vector>

using namespace std;

int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		vector<int> octal;
		while(n > 0){
			octal.push_back(n % 8);
			n /= 8;
		}
		for(int i=octal.size()-1; i>=0; i--){
			printf("%d", octal[i]);
		}
		printf("\n");
	} 
	return 0;
}

习题6.2 又一版A+B

提交网址

#include <iostream>
#include <vector>

using namespace std;

int main(){
	long long A, B, sum;
	int m;
	while(scanf("%d", &m) != EOF){
		if(m == 0) break;
		scanf("%lld%lld", &A, &B);
		sum = A + B;
		vector<int> num;
		if(sum == 0) num.push_back(0);	//需要单独讨论 
		while(sum > 0){
			num.push_back(sum % m);
			sum /= m;
		}
		for(int i=num.size()-1; i>=0; i--){
			printf("%d", num[i]);
		}
		printf("\n");
	} 
	return 0;
}

习题6.3 进制转换

提交网址
极简方法:scanf 和 printf 的标准输入输出
%x:以16进制数的方式,从键盘输入一个整数存到变量中
%d:以10进制数的方式

#include <iostream>

using namespace std;

int main(){
	int num;
	while(scanf("%x", &num) != EOF){
		printf("%d\n", num);
	}
	return 0;
}

这一题题目并没有说明输入和输出是否超过了整型的最大长度。如果超过了应该使用字符串乘法(如上面的例题6.3);实际操作证明系统的测试用例没有超过长整型的范围。
按照字符串输入输出方式:

#include <iostream>
#include <cstring>

using namespace std;

int main(){
	string hex;
	while(cin >> hex){
		hex = hex.substr(2);
		long long oct = 0;
		for(int i=0; i<hex.size(); i++){
			if(hex[i] >= '0' && hex[i] <= '9'){
				oct = oct * 16 + hex[i] - '0';
			}else if(hex[i] >= 'A' && hex[i] <= 'Z'){
				oct = oct * 16 + hex[i] - 'A' + 10;
			}
		}
		printf("%lld\n", oct);
	}
	return 0;
}

习题6.4 数制转换

提交网址
和例题6.4一样,多了个大小写而已。

#include <iostream>
#include <cstring>
#include <stack>

using namespace std;

int main(){
	int a, b;
	string n;
	while(cin >> a >> n >> b){
		long long num = 0;
		stack<char> out;
		for(int i=0; i<n.length(); i++){
			if(n[i] >= '0' && n[i] <= '9'){
				num = num * a + n[i] - '0';
			}else if(n[i] >= 'A' && n[i] <= 'Z'){
				num = num * a + n[i] - 'A' + 10;
			}else if(n[i] >= 'a' && n[i] <= 'z'){
				num = num * a + n[i] - 'a' + 10;
			}
		}
		while(num > 0){
			int tmp = num % b;
			if(tmp >= 10){
				out.push(tmp - 10 + 'A');
			}else{
				out.push(tmp + '0');	
			}
			num /= b;
		}
		while(!out.empty()){
			printf("%c", out.top());
			out.pop(); 
		}
		printf("\n");
	}
	return 0;
}

6.2 最大公约数和最小公倍数

最大公约数和最小公倍数

1. 最大公约数(Greatest Common Divisor, GCD)

求最大公约数的最常用方法:欧几里得算法,又称辗转相除法

例题6.5 最大公约数

提交网址

#include <iostream>
using namespace std;

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

int main(){
	int a, b;
	while(scanf("%d%d", &a, &b) != EOF){
		printf("%d\n", GCD(a, b));
	}
	return 0;
}

2. 最小公倍数(Least Common Multiple, LCM)

a, b两个数的最小公倍数为两数的乘积除以它们的最大公约数。

例题6.6 最小公倍数

#include <iostream>
using namespace std;

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

int main(){
	int a, b;
	while(scanf("%d%d", &a, &b) != EOF){
		printf("%d\n", a * b / GCD(a, b));
	}
	return 0;
}

习题6.5 最简真分数

提交网址

#include <iostream>
using namespace std;

int GCD(int x, int y){
	if(y == 0){
		return x;
	}
	return GCD(y, x % y);
}

int main(){
	int n, a[600];
	while(scanf("%d", &n) != EOF){
		if(n == 0) break;
		for(int i=0; i<n; i++){
			scanf("%d", &a[i]);
		}
		int cnt = 0;
		for(int i=0; i<n; i++){
			for(int j=0; j<n; j++){
				if(a[i] < a[j] && GCD(a[i], a[j]) == 1){
					cnt++;
				}
			}
		}
		printf("%d\n", cnt);
	}
	return 0;
}

6.3 质数

例题6.7 素数判定

用 1 到 sqrt(n) 的数试着整除该整数。
提交网址

#include <iostream>
#include <cmath> 
using namespace std;

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

int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		if(Judge(n)){
			printf("yes\n");
		}else{
			printf("no\n");
		}
	}
	return 0;
}

素数筛法:从2开始遍历从2到1000000的所有整数,若当前整数没有因为它是某个小于其的素数的倍数而被标记为非素数,则判定其为素数,并标记它所有的倍数为非素数。然后遍历下一个数,直到遍历完2到1000000内的所有整数。此时,所有未标记成非素数的数即为要求的素数。

例题6.8 素数

提交网址

#include <iostream>
#include <vector>
using namespace std;

const int MAXN = 10001;

vector<int> prime;
bool isPrime[MAXN];

void Initial(){
	for(int i=0; i<MAXN; i++){
		isPrime[i] = true;
	}
	isPrime[0] = false;
	isPrime[1] = false;
	for(int i=2; i<MAXN; i++){
		if(!isPrime[i]) continue;
		prime.push_back(i);
		for(int j=i*i; j<MAXN; j+=i){
			isPrime[j] = false;
		}
	}
}

int main(){
	Initial();
	int n;
	while(scanf("%d", &n) != EOF){
		bool isOutput = false;
		for(int i=0; i<prime.size() && prime[i] < n; i++){
			if(prime[i] % 10 == 1){
				isOutput = true;
				printf("%d ", prime[i]);
			}
		}
		if(!isOutput){
			printf("-1");
		}
		printf("\n");
	}
	return 0;
}

习题6.6 Prime Number

提交网址

#include <iostream>
#include <vector>
using namespace std;

const int MAXN = 1000000;
bool isPrime[MAXN];
vector<int> prime;

void Init(){
	for(int i=0; i<MAXN; i++){
		isPrime[i] = true;
	}
	isPrime[0] = isPrime[1] = false;
	for(int i=2; i<MAXN; i++){
		if(!isPrime[i]){
			continue;
		}
		prime.push_back(i);
		for(int j=i*i; j<MAXN; j+=i){
			isPrime[j] = false;
		}
	}
	return;
}

int main(){
	Init();
	int k;
	while(scanf("%d", &k) != EOF){
		printf("%d\n", prime[k-1]);
	}
	return 0;
}

6.4 分解质因数

例题6.9 质因数的个数

提交网址
判断质因数的时候不需要先判断一个因子是否为质数!

#include <iostream>
#include <cmath>

using namespace std;

int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		int bound = sqrt(n);
		int cnt = 0;
		for(int i=2; i<=bound; i++){
			if(n == 1) break;
			while(n % i == 0){
				cnt++;
				n /= i;
			}
		}
		//存在大于sqrt(n)的因子 
		if(n > 1) cnt++;
		printf("%d\n", cnt);
	} 
	return 0;
}

习题6.7 约数的个数

提交网址

#include <iostream>
#include <cmath>

using namespace std;

int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		for(int i=0; i<n; i++){
			int cnt = 0, tmp;
			scanf("%d", &tmp);
			int bound = sqrt(tmp);
			for(int i=1; i<bound; i++){
				if(tmp % i == 0) cnt += 2;
			}
			if(bound*bound == tmp){
				cnt++;
			}else if(tmp % bound == 0){
				cnt += 2;
			}
			printf("%d\n", cnt);
		}
	}
	return 0;
}

习题6.8 整除问题

提交网址

#include <bits/stdc++.h>

using namespace std;

int main(){
	int n, a;
	scanf("%d%d", &n, &a);
	vector<int> prime, num1, num2;
	for(int i=2; i<=a; i++){
		int cnt = 0;
		if(a % i == 0){
			prime.push_back(i);
			cnt = 1;
			a /= i;
		}
		while(a % i == 0){
			cnt++;
			a /= i;
		}
		if(cnt > 0){
			num1.push_back(cnt);
		}
		if(a == 1) break;
	}
	for(int i=0; i<num1.size(); i++){
		num2.push_back(0);
	}
	for(int i=2; i<=n; i++){
		int tmp = i;
		for(int j=0; j<num1.size(); j++){
			while(tmp % prime[j] == 0){
				num2[j]++;
				tmp /= prime[j];
			}
		}
	}
	int k = num2[0] / num1[0];
	for(int i=1; i<num1.size(); i++){
		k = min(k, num2[i]/num1[i]);
	}
	printf("%d\n", k);
	return 0;
}

6.5 快速幂

任何一个数字n都可以分解为若干个2k之和。因此,可以先将指数b分解为若干个2k之和,即求b的二进制数。

例题6.10 人见人爱A^B

#include <iostream>
using namespace std;

int FastExponentation(int a, int b, int mod){
	int answer = 1;
	while(b != 0){
		if(b % 2 == 1){
			answer *= a;
			answer %= mod;
		}
		b /= 2;
		a *= a;
		a %= mod;
	}
	return answer;
}

int main(){
	int a, b;
	while(scanf("%d%d", &a, &b) != EOF){
		if(a == 0 && b == 0) break;
		printf("%d\n", FastExponentation(a, b, 1000));
	}
	return 0;
}

习题6.9 求root(N, k)

提交网址
这一题需要一定的数学推导,具体可以看题解和讨论板块。
理解原理之后比较好做,但是一般没碰到过这题应该是比较不好想出来的。(至少我应该想不上去唉

#include <iostream>
using namespace std;

int FastExponentation(long long x, long long y, int mod){
	int answer = 1;
	while(y != 0){
		if(y % 2 == 1) answer = (answer * x) % mod;
		x = (x * x) % mod;
		y /= 2;
	}
	return answer ? answer : mod;
} 

int main(){
	long long x, y;
	int k;
	while(scanf("%lld%lld%d", &x, &y, &k) != EOF){
		printf("%d\n", FastExponentation(x, y, k-1));
	}
	return 0;
}

6.6 矩阵与矩阵快速幂

有一说一,正常用矩阵存储处理起来,真的比稀疏矩阵压缩的三元组存储好做多了。(详见数据结构noj)

1. 矩阵

例题6.11 两个矩阵的乘积

提交网址

#include <iostream>
using namespace std;

int main(){
	int a[2][3], b[3][2], c[2][2];
	for(int i=0; i<2; i++){
		for(int j=0; j<3; j++){
			scanf("%d", &a[i][j]);
		}
	}
	for(int i=0; i<3; i++){
		for(int j=0; j<2; j++){
			scanf("%d", &b[i][j]);
		}
	}
	for(int i=0; i<2; i++){
		for(int j=0; j<2; j++){
			c[i][j] = 0;
			for(int k=0; k<3; k++){
				c[i][j] += a[i][k] * b[k][j];
			}
			printf("%d ", c[i][j]);
		}
		printf("\n");
	}
	return 0;
}

2. 矩阵快速幂

类似于数字的快速幂。区别在于,对数字的快速幂而言,其初始值为1;而对矩阵的快速幂而言,其初始值是单位矩阵

例题6.12 矩阵幂

提交网址

#include <iostream>
using namespace std;

struct Matrix{
	int matrix[10][10];
	int row, col;
	Matrix(int r, int c): row(r), col(c) {};
};

Matrix Multiply(Matrix x, Matrix y){
	Matrix answer(x.row, y.col);
	for(int i=0; i<x.row; i++){
		for(int j=0; j<y.col; j++){
			answer.matrix[i][j] = 0;
			for(int k=0; k<x.col; k++){
				answer.matrix[i][j] += x.matrix[i][k] * y.matrix[k][j];
			}
		}
	}
	return answer;
}

Matrix FastExponentation(Matrix x, int k){
	Matrix answer(x.row, x.col);
	for(int i=0; i<x.row; i++){
		for(int j=0; j<x.col; j++){
			if(i == j){
				answer.matrix[i][j] = 1;
			}else{
				answer.matrix[i][j] = 0;
			}
		}
	}
	while(k != 0){
		if(k % 2 == 1){
			answer = Multiply(answer, x); 
		}
		k /= 2;
		x = Multiply(x, x);
	}
	return answer;
}

void PrintMatrix(Matrix x){
	for(int i=0; i<x.row; i++){
		for(int j=0; j<x.col; j++){
			if(j == x.col - 1){
				printf("%d\n", x.matrix[i][j]);
			}else{
				printf("%d ", x.matrix[i][j]);
			}
		}
	}
}

int main(){ 
//	这里是按照测试用例的形式写的,结果不能AC
//	输入的方法和测试用例不一样。。。 
//	int caseNumber;
//	scanf("%d", &caseNumber);
//	while(caseNumber--){
//		int n, k;
//		scanf("%d%d", &n, &k);
//		Matrix x(n, n);
//		for(int i=0; i<n; i++){
//			for(int j=0; j<n; j++){
//				scanf("%d", &x.matrix[i][j]);
//			}
//		}
//		Matrix answer = FastExponentation(x, k);
//		PrintMatrix(answer);
//	}
	int n, k;
	while(scanf("%d%d", &n, &k) != EOF){
		Matrix x(n, n);
		for(int i=0; i<n; i++){
			for(int j=0; j<n; j++){
				scanf("%d", &x.matrix[i][j]);
			}
		}
		Matrix answer = FastExponentation(x, k);
		PrintMatrix(answer);		
	}
	return 0;
}

习题6.10 A + B for Matrices

提交网址

#include <iostream>
using namespace std;

int main(){
	int m, n;
	int a[10][10], b[10][10], c[10][10];
	while(scanf("%d", &m) != EOF){
		if(m == 0) break;
		scanf("%d", &n);
		for(int i=0; i<m; i++){
			for(int j=0; j<n; j++){
				scanf("%d", &a[i][j]);
			}
		}
		for(int i=0; i<m; i++){
			for(int j=0; j<n; j++){
				scanf("%d", &b[i][j]);
			}
		}
		for(int i=0; i<m; i++){
			for(int j=0; j<n; j++){
				c[i][j] = a[i][j] + b[i][j];
			}
		}
		int cnt = 0;
		for(int i=0; i<m; i++){
			int j = 0;
			for( ; j<n; j++){
				if(c[i][j] != 0){
					break;
				}
			}
			if(j == n) cnt++;
		}
		for(int j=0; j<n; j++){
			int i = 0;
			for( ; i<m; i++){
				if(c[i][j] != 0){
					break;
				}
			}
			if(i == m) cnt++;
		}
		printf("%d\n", cnt);
	} 
	return 0;
}

习题6.11 递推数列

提交网址

#include <iostream>
#include <vector>
using namespace std;

int main(){
	vector<int> a;
	int p, q, k, a1, a2;
	scanf("%d%d%d%d%d", &a1, &a2, &p, &q, &k);
	a.push_back(a1);
	a.push_back(a2);
	int i = 1;
	while(i < k){
		i++;
		a[i] = (p * a[i-1] + q * a[i-2]) % 10000;
	}
	printf("%d\n", a[k] % 10000);
	return 0;
}

6.7 高精度整数

书上说照着模板敲!(但是考试能看书吗 orz
大数加法 其实之前的字符串章节做过类似的题目了,就是考察字符串操作而已。自己敲一遍吧。
提交网址

//我就假装都是整数咯!
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;

int main(){
	string a, b;
	while(cin >> a >> b){
		vector<int> c;
		if(a.length() < b.length()){
			a.insert(0, b.length()-a.length(), '0');
		}else if(a.length() > b.length()){
			b.insert(0, a.length()-b.length(), '0');
		}
		int carry = 0;
		int len = a.length();
		for(int i=len-1; i>=0; i--){
			int tmp = a[i] - '0' + b[i] - '0' + carry;
			carry = tmp / 10;
			tmp %= 10;
			c.push_back(tmp);
		}
		if(carry == 1) c.push_back(1);
		for(int i=c.size()-1; i>=0; i--){
			printf("%d", c[i]);
		}
		printf("\n");
	}
	return 0;
}

例题6.14 N的阶乘

提交网址
大数乘法 这一题乘法的方法也可以理解掌握一下,比较巧妙,没有用字符串而是用了数组。

#include <iostream>
using namespace std;

const int MAXN = 10000;

int main(){
	int a[MAXN] = {0};
	int n;
	while(scanf("%d", &n) != EOF){
		int carry = 0, p = 1;
		a[0] = 1;
		for(int i=2; i<=n; i++){
			int j;
			for(j=0; j<p; j++){
				int tmp = a[j] * i + carry;
				carry = tmp / 10;
				a[j] = tmp % 10;
			}
			while(carry){
				a[j] = carry % 10;
				carry /= 10;
				j++;
			}
			p = j;
		}
		for(int i=p-1; i>=0; i--){
			printf("%d", a[i]);
		}
		printf("\n");
	}
	return 0;
}

习题6.12 数字阶梯求和

提交网址
用字符串来存,代码如下:

#include <iostream>
#include <string>
using namespace std;

int main(){
	int a, n;
	while(scanf("%d%d", &a, &n) != EOF){
		string res = "";
		int carry = 0;
		for(int i=n; i>=1; i--){
			int tmp = i * a + carry;
			char c = tmp % 10 + '0';
			res = c + res;
			carry = tmp / 10;
		}
		if(carry){
			printf("%d", carry);
		}
		cout << res << endl;
		printf("\n");
	}
	return 0;
}

本来是用的数组存的,但是一直报段错误。虽然看不到错误用例吧,我后来改了一下代码,认为应该是用例中n是可以超过100的,和题目说的不一样。果然,改了之后就正确了。。。
下面是数组存放的AC代码。

#include <iostream>
#include <string>
using namespace std;

int main(){
	int a, n;
	while(scanf("%d%d", &a, &n) != EOF){
		int sum[n+1];
		int carry = 0;
		for(int i=n; i>=1; i--){
			int tmp = i * a + carry;
			sum[n-i] = tmp % 10;
			carry = tmp / 10;
		}
		if(carry){
			sum[n++] = carry;
		}
		for(int i=n-1; i>=0; i--){
			printf("%d", sum[i]);
		}
		printf("\n");
	}
	return 0;
}

习题6.13 大整数的因子

提交网址
大数除法 也可以学习掌握一下!

#include <iostream>
#include <string>
#include <vector>
using namespace std;

bool IsFactor(string num, int k){
	int remainder = 0;
	for(int i=0; i<num.length(); i++){
		remainder = (remainder * 10 + num[i] - '0') % k; 
	}
	if(remainder == 0) return true;
	return false;
}

int main(){
	string num;
	while(cin >> num){
		if(num[0] == '-') break;
		vector<int> factor;
		for(int i=2; i<=9; i++){
			if(IsFactor(num, i)){
				factor.push_back(i);
			}
		}
		if(factor.size() == 0){
			printf("none\n");
		}else{
			for(int i=0; i<factor.size()-1; i++){
				printf("%d ", factor[i]);
			}			
			printf("%d\n", factor[factor.size() - 1]);		
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值