《算法笔记》学习日记——5.2 最大公约数与最小公倍数&5.3 分数的四则运算&5.4 素数

5.2 最大公约数与最小公倍数

Codeup Contest ID:100000589

问题 A: Least Common Multiple

题目描述
The least common multiple (LCM) of a set of positive integers is the smallest positive integer which is divisible by all the numbers in the set. For example, the LCM of 5, 7 and 15 is 105.
输入
Input will consist of multiple problem instances. The first line of the input will contain a single integer indicating the number of problem instances. Each instance will consist of a single line of the form m n1 n2 n3 … nm where m is the number of integers in the set and n1 … nm are the integers. All integers will be positive and lie within the range of a 32-bit integer.
输出
For each problem instance, output a single line containing the corresponding LCM. All results will lie in the range of a 32-bit integer.
样例输入

2
2 3 5
3 4 6 12

样例输出

15
12

思路
这题是计算多个数的最小公倍数,首先当然要明白多个数的最小公倍数怎么算,以三个数为例,先计算前两个数的最小公倍数,再计算前两个数的最小公倍数和第三个数的最大公约数,再通过公式计算出新的最小公倍数就是答案了。
另外,当m=1的时候,输出本身即可。
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<algorithm>
using namespace std;
int gcd(int a, int b){//求最大公约数 
	if(b==0) return a;
	else return gcd(b, a%b);
}
int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		while(n--){
			int m, x, y;
			scanf("%d", &m);
			if(m==1){
				int tmp;
				scanf("%d", &tmp);
				printf("%d\n", tmp);	
			}
			else{
				scanf("%d", &x);
				scanf("%d", &y);
				int d = gcd(x, y);
				int temp = (x/d)*y;//先计算前两个数的最小公倍数 
				for(int i=2;i<m;i++){
					int z;
					scanf("%d", &z);
					d = gcd(temp, z);//把最小公倍数当成一个因子,计算和下一个数的最大公约数
					temp = (temp/d)*z;//再根据公式算出新的最小公倍数 
				}
				printf("%d\n", temp);	
			}
		}
	}
} 

小结

这一类的题目记住辗转相除法的函数基本上也都能解决了,主要是弄清楚三个数及以上的最大公约数、最小公倍数怎么求(先求前两个数的最大公约数/最小公倍数,再把它作为新的数和下一个数继续运算求最大公约数/最小公倍数)。

5.3 分数的四则运算

Codeup Contest ID:100000590

问题 A: 分数矩阵

题目描述
我们定义如下矩阵:
1/1 1/2 1/3
1/2 1/1 1/2
1/3 1/2 1/1
矩阵对角线上的元素始终是1/1,对角线两边分数的分母逐个递增。
请求出这个矩阵的总和。
输入
输入包含多组测试数据。每行给定整数N(N<50000),表示矩阵为N*N。当N=0时,输入结束。
输出
输出答案,结果保留2位小数。
样例输入

1
2
3
4
0

样例输出

1.00
3.00
5.67
8.83

思路
这题有三个坑:
①不能把这个矩阵定义成二维数组,50000×50000编译器都不会让你通过;
②不能按照题目意思一行一行算,这样肯定会时间超限(比如先用for循环遍历每一行,再用for循环遍历每一行的每一个元素,那时间复杂度可是O(N^2));
③不要被《算法笔记》上的定义分数结构体给误导了。
我就是一开始定义了分数,并且写了个分数加法的函数(没写约分),结果发现一直是答案错误50,怎么都过不了,后来单步调试了一下才发现当N=170左右的时候吧,因为分数加法的分母是相乘的,所以最后加出来分数的分母会巨大无比,直到溢出为0,最后答案就输出-1.#J(这是除0的输出),可能写了约分函数不会出现这种情况(我没去试),于是干脆就全部用double型来表示了,最后终于正确100了T T。
这还只是第9个元素之和的时候,sum.down(和的分母)已经巨大无比了
[这还只是第9个元素之和的时候,sum.down(和的分母)已经巨大无比了]
好了那么这题的思路很简单,聪明的我们当然不能被题目所迷惑,什么对角线两边分数的分母逐个递增,这都是骗你的。
你把矩阵写完,沿着对角线劈开,很容易就发现规律了。比如题目给的3×3矩阵,那半个矩阵(沿着对角线劈开的那半个)中的元素就是1个1/3,2个1/2,3个1/1,同理,N×N的矩阵的话,半个矩阵就是1个1/N,2个1/(N-1),…,N个1/1,于是只要写一个for循环把这些元素全部加起来即可,最后的结果乘2减N就行了(因为乘2的话会算两遍对角线,而对角线上的和正好是N,所以要减去一个N)。
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<algorithm>
using namespace std;
int main(){
	int N;
	while(scanf("%d", &N) != EOF){
		if(N==0) break;
		double result = 0;
		if(N==1) result = 1;
		else{
			double sum = 0;
			for(int i=1, j=N;i<=N, j>=1;i++, j--) sum += i*(1.0/j);
			result = 2*sum-N;
		}
		printf("%.2f\n", result);
	}
} 

小结

分数的四则运算大家肯定都知道,如果题目硬性要求输出分数类的格式的话,还是要定义一个分数的结构体去做,而且最好也要把约分的函数也写上,不然很容易就超数据类型的范围了(比如上一题),但是如果题目只是要求用分数的形式让你计算一些东西,其实也完全可以不用定义分数结构体,直接找规律就能得出答案了(比如上一题)。

5.4 素数

Codeup Contest ID:100000591

问题 A: 素数

题目描述
输入一个整数n(2<=n<=10000),要求输出所有从1到这个整数之间(不包括1和这个整数)个位为1的素数,如果没有则输出-1。
输入
输入有多组数据。
每组一行,输入n。
输出
输出所有从1到这个整数之间(不包括1和这个整数)个位为1的素数(素数之间用空格隔开,最后一个素数后面没有空格),如果没有则输出-1。
样例输入

70

样例输出

11 31 41 61

思路
这题花了很久很久很久的时间,主要是codeup答案错误50不知道错哪了……
思路很简单,用筛法也好,用普通的选素数方法也好,把范围内的素数挑出来,然后再根据题意再把个位为1的素数选出来,就是题目要求的答案了。
我这里用了埃氏筛法来做,一开始的算法是根据输入的整数n在[2,n-1]的范围内筛选素数,然后再挑选个位为1的数,牛客网一遍AC过,codeup死活过不了。
后来对比了网上的AC代码,把算法改成了在输入n之前预处理好maxn范围内的所有素数,再从素数表中挑选个位为1的数,codeup才AC(心累┓(;´_`)┏)。
如果可以的话请各位大佬告诉我原算法是哪儿没考虑到(跪求)。
代码

/////原算法(在输入n之后处理[2,n-1]之内的素数,牛客网AC,但是codeup答案错误50)/////
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<algorithm>
using namespace std;
const int maxn = 10001;
bool p[maxn]={0};
int prime[maxn], pNum = 0;
bool judge(int a){
	char temp[6]={0};
	sprintf(temp, "%d", a);
	if(temp[strlen(temp)-1]=='1') return true;//末尾是1,返回true 
	else return false;//否则返回false 
}
int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		for(int i=2;i<n;i++){
			if(p[i]==false){//埃氏筛法
				prime[pNum++] = i;
				for(int j=i+i;j<n;j+=i) p[j] = true;
			}
		}
		int cnt = 0;
		for(int i=0;i<pNum;i++){//此时的prime里是2~n-1之间的所有素数 
			if(judge(prime[i])==true){//再筛选末尾是1的数 
				cnt++;
				if(cnt==1) printf("%d", prime[i]);
				else printf(" %d", prime[i]);
			}
		}
		if(cnt==0) printf("-1\n");
		else printf("\n");
		memset(prime, 0, sizeof(prime));
		memset(p, 0, sizeof(p));
	}
	return 0;
}
/////预处理算法(在输入n之前把maxn内的所有素数全部打成素数表,牛客网&&codeup AC)/////
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<algorithm>
using namespace std;
const int maxn = 10001;
bool p[maxn]={0};
int prime[maxn]={0}, pNum = 0;
bool judge(int a){
	char temp[6]={0};
	sprintf(temp, "%d", a);
	if(temp[strlen(temp)-1]=='1') return true;//末尾是1,返回true 
	else return false;//否则返回false 
}
int main(){
	for(int i=2;i<maxn;i++){
		if(p[i]==false){//埃氏筛法预处理出maxn内的所有素数 
			prime[pNum++] = i;
			for(int j=i+i;j<maxn;j+=i) p[j] = true;
		}
	}
	int n;
	while(scanf("%d", &n) != EOF){
		int cnt = 0;//记录满足题意素数的个数 
		for(int i=0;i<pNum;i++){//在素数表中挑 
			if(prime[i]<n&&judge(prime[i])==true){//如果小于n并且个位数是1 
				cnt++;
				if(cnt==1) printf("%d", prime[i]);//如果是第一个素数,直接输出 
				else printf(" %d", prime[i]);//如果不是,先输出空格 
			}
		}
		if(cnt==0) printf("-1\n");
		else printf("\n");
	}
	return 0;
}

问题 B: Prime Number

题目描述
Output the k-th prime number.
输入
k≤10000
输出
The k-th prime number.
样例输入

10
50

样例输出

29
229

思路
用筛法预处理出所有素数,然后根据题目输入直接查找对应的素数即可,这里要注意存储的素数表下标是从0开始的,因此,第k个素数对应的下标应该是k-1,而且这里的k最大能到10000(也就是说要保证有第10000个素数的存在,所以maxn要开大一点,我试了maxn=10万是不够的,开到100万就够了)。
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<algorithm>
using namespace std;
const int maxn = 1000000;
bool p[maxn]={0};
int prime[maxn]={0}, pNum = 0;
int main(){
	for(int i=2;i<maxn;i++){
		if(p[i]==false){
			prime[pNum++] = i;
			for(int j=i+i;j<maxn;j+=i) p[j] = true;
		}
	}
	int k;
	while(scanf("%d", &k) != EOF){
		printf("%d\n", prime[k-1]);
		//因为prime的下标从0开始,所以第k个素数对应的下标应该是k-1 
	}
	return 0;
}

问题 C: Goldbach’s Conjecture

题目描述
Goldbach’s Conjecture: For any even number n greater than or equal to 4, there exists at least one pair of prime numbers p1 and p2 such that n = p1 + p2.
This conjecture has not been proved nor refused yet. No one is sure whether this conjecture actually holds. However, one can find such a pair of prime numbers, if any, for a given even number. The problem here is to write a program that reports the number of all the pairs of prime numbers satisfying the condition in the conjecture for a given even number.
A sequence of even numbers is given as input. Corresponding to each number, the program should output the number of pairs mentioned above. Notice that we are interested in the number of essentially different pairs and therefore you should not count (p1, p2) and (p2, p1) separately as two different pairs.
输入
An integer is given in each input line. You may assume that each integer is even, and is greater than or equal to 4 and less than 215. The end of the input is indicated by a number 0.
输出
Each output line should contain an integer number. No other characters should appear in the output.
样例输入

4
10
16
0

样例输出

1
2
2

思路
这题的题意大致是给你一个数,然后让你输出有几组两个素数相加能得到该数的方案。
我的思路很简单,先预处理好素数表(其实主要是为了得到散列表p),然后从2到n/2开始遍历(因为p1+p2和p2+p1算一种方案,所以只需要遍历一半),如果p[i]和p[n-i]都属于素数,那么方案数加1,最后直接输出方案数即可。
代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<algorithm>
using namespace std;
const int maxn = 40000;
bool p[maxn]={0};
int prime[maxn]={0}, pNum = 0;
int main(){
	for(int i=2;i<maxn;i++){
		if(p[i]==false){
			prime[pNum++] = i;
			for(int j=i+i;j<maxn;j+=i) p[j] = true;
		}
	}
	int n;
	while(scanf("%d", &n) != EOF){
		if(n==0) break;
		int cnt = 0;
		for(int i=2;i<=n/2;i++){//只遍历一半,因为(p1, p2)和(p2, p1)只算一种方案 
			if(p[i]==false&&p[n-i]==false) cnt++;
			//如果两者都未被筛去(说明都是素数),则方案数加1 
		}
		printf("%d\n", cnt);
	}
	return 0;
}

小结

素数的题目也是比较简单的,先把素数表用筛法预处理之后,基本上所有问题都能迎刃而解。最重要的一点是做的时候要细心一些,不然有些测试点一直卡着也会挺恼人的。

发布了54 篇原创文章 · 获赞 27 · 访问量 4995

猜你喜欢

转载自blog.csdn.net/weixin_42257812/article/details/105143355
今日推荐