考研复试系列——第九节 数论基础
引言
首先引入一道简单的题目来说明一下最近看到的一个小技巧 ,觉得挺不错的 ,
该部分内容来源于 《王道论坛》 。
写个算法,对 2 个小于 1000000000 的输入,求结果。
特殊乘法举例:123 * 45 = 1*4 +1*5 +2*4 +2*5 +3*4+3*5
样例输入:
123 45
样例输出:
54
这道题目肯很简单,我们一眼望去想到的思路就是无非是将每一位求出保存起来,然后双重for循环不就好了。
代码如下:
#include<iostream> using namespace std; int main() { int a,b; while(cin>>a>>b && a && b) { int buff1[10],buff2[10]; int size1=0,size2=0; while(a!=0) { buff1[size1++] = a % 10; a/=10; } while(b!=0) { buff2[size2++] = b % 10; b/=10; } int ans = 0; for(int i=0;i<size1;i++) for(int j=0;j<size2;j++) ans += buff1[i] * buff2[j]; cout<<ans<<endl; } return 0; }换种思路,加入我们把输入的整数当做字符串输入,处理起来不是简单多了,就不用再一位一位的求保存起来,而可以直接处理。
#include<iostream> using namespace std; int main() { char a[11],b[11]; while(cin>>a>>b && a && b) { int ans = 0; for(int i=0;a[i]!='\0';i++) for(int j=0;b[j]!='\0';j++) ans += (a[i] - '0') * (b[j] - '0'); cout<<ans<<endl; } return 0; }
这道题本身没有难度,但是通过字符串输入解决问题的思路值得我们学习,想想写大数加法乘法用的不就是这种思路吗。
素数问题
如何判断一个素数 ,这个相信大家都会了,但其常常是其他素数问题的基础,我们再来写一下。
#include<iostream> #include<cmath> using namespace std; bool checkPrime(int n) { if(n <= 1) return false; int bound = sqrt(n); for(int i=2;i<=bound;i++) if(n%2 == 0) return false; return true; } int main() { int n; cin>>n; if(checkPrime(n)) cout<<n<<"是素数"<<endl; else cout<<n<<"不是素数"<<endl; return 0; }
素数筛选法
假如我们要求出2到100000之间的所有素数,使用前面的方法一个个判断非常不可,但是从效率上肯定不高。我们考虑这样一种策略,
当我们得到一个素数,那他的倍数肯定不是素数,就把它的倍数标记为非素数,这样当我们遍历到一个数时,它没有被任何小于它的素数
标记为非素数,我们就可以确定这个数是素数。简单举个例子,我们从2开始,2是素数,于是将2的倍数也就是4,6,8,。。。标记为非素数
然后到3,因为3没有被小于它的素数2标记,所以3是素数,于是又将9,12,15.。。。标记为非素数,以此类推我们就得到了一张素数表。
#include<iostream> #include<cmath> using namespace std; #define MAX 100001 int prime[MAX]; void Prime() { int bound = sqrt(MAX); for(int i=2;i<=bound;i++) { if(prime[i] == 0)//如果没有被标记 { for(int j=i*i;j<MAX;j+=i)//这里为什么是i*i要注意,比如当前是9,那么5*9 ,7*9肯定在前面以及被标记过了,所以最小是9*9 prime[j] = 1; } } } int main() { Prime(); for(int i=2;i<MAX;i++) { if(prime[i] == 0) cout<<i<<" "; } cout<<endl; return 0; }
下面来一道例题:
描述:
现在给出你一些数,要求你写出一个程序,输出这些整数相邻最近的素数,并输出其相距长度。如果左右有等距离长度素数,则输出左侧的值及相应距离。
如果输入的整数本身就是素数,则输出该素数本身,距离输出0
输入:
第一行给出测试数据组数N(0<N<=10000)
接下来的N行每行有一个整数M(0<M<1000000),
输出:
每行输出两个整数 A B.
其中A表示离相应测试数据最近的素数,B表示其间的距离。
现在给出你一些数,要求你写出一个程序,输出这些整数相邻最近的素数,并输出其相距长度。如果左右有等距离长度素数,则输出左侧的值及相应距离。
如果输入的整数本身就是素数,则输出该素数本身,距离输出0
输入:
第一行给出测试数据组数N(0<N<=10000)
接下来的N行每行有一个整数M(0<M<1000000),
输出:
每行输出两个整数 A B.
其中A表示离相应测试数据最近的素数,B表示其间的距离。
sample input:
3
6
8
10
sample output:
5 1
7 1
11 1
7 1
11 1
注意:距离999999最近的素数就是1000003
#include<iostream> #include<cmath> using namespace std; #define MAX 1000010 int prime[MAX]; void Prime()//求素数表 { prime[0] = prime[1] = 1; int bound = sqrt(MAX); for(int i=2;i<=bound;i++) { if(prime[i] == 0)//如果没有被标记 { for(int j=i*i;j<MAX;j+=i)//这里为什么是i*i要注意,比如当前是9,那么5*9 ,7*9肯定在前面以及被标记过了,所以最小是9*9 prime[j] = 1; } } } int main() { int N,M; Prime(); cin>>N; while(N--) { cin>>M; if(prime[M] == 0){ cout<<M<<" 0"<<endl; } else{ int RIndex = M; int LIndex = M; while(prime[LIndex] == 1 && LIndex >= 0)//寻找左边素数 LIndex --; while(prime[RIndex] == 1)//寻找右边素数 RIndex ++; if(LIndex < 0)//左边没有找到 cout<<RIndex<<" "<<RIndex-M<<endl; else if(M - LIndex <= RIndex - M)//左右都有且左边距离更近 cout<<LIndex<<" "<<M-LIndex<<endl; else cout<<RIndex<<" "<<RIndex-M<<endl;//左右都有且右边距离更近 } } return 0; }
二分法求解方程组
题目如下:求f(x)=x^3-x-1在【1,1.5】内的一个实根,使误差不超过0.005。结果保留两位小数。
二分法算法思想:首先确定有根区间,将区间二等分,通过判断f(x)的符号,逐步将有根区间缩小,直至有根区间足够小,便可求出满足精度要求的近似值。
如图所示:
根据流程图写代码:
#include<iostream> #include<iomanip> #include<cmath> using namespace std; float calculate(float x) { return x*x*x - x - 1; } int main() { float a,b,c,x;//输入区间以及精度 cin>>a>>b>>c; while(fabs(b-a)>=c) { x = (a+b)/2; if(calculate(a) * calculate(x) < 0) b = x; else a = x; } cout<<"满足条件的值为:"<<fixed<<setprecision(2)<<x<<endl; return 0; }
求三角形的面积
直接套公式就好了 ,高中数学就讲过。
#include<iostream> #include<cmath> using namespace std; float area3(float x1,float y1,float x2,float y2,float x3,float y3)//这里用的坐标,也可以直接根据边长 { float a,b,c,p; a=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); b=sqrt((x1-x3)*(x1-x3)+(y1-y3)*(y1-y3)); c=sqrt((x3-x2)*(x3-x2)+(y3-y2)*(y3-y2)); p=(a+b+c)/2; return sqrt(p*(p-a)*(p-b)*(p-c)); } int main() { float x1,x2,x3,y1,y2,y3; cin>>x1>>y1>>x2>>y2>>x3>>y3; cout<<area3(x1,y1,x2,y2,x3,y3)<<endl; return 0; }
进制转换
先来一道很简单的题目:输入一个十进制数,将其转换为8进制数并输出 (某某年中科大的上机试题之一)。#include<iostream> #include<stack> using namespace std; stack<int> s; void convert(int n)//将十进制数转换为8进制 { while(n) { s.push(n%8); n /= 8; } } int main() { int n;//十进制数 cin>>n; convert(n); while(!s.empty()) { int a = s.top(); cout<<a; s.pop(); } cout<<endl; return 0; }注意 :这里没有考虑0和大整数的问题。
再来看一道题目
10进制转2进制并判断二进制数中1的个数,也是中科大某某年的上机试题。这道也不难。我们可以与上题一样,但是在出栈时判断一下是1就好了。
这里就不给代码了,但我们也可以采取另一种方式直接计算二进制中1的个数。利用移位操作和与运算就能方便的计算了,这个也很简单。记得某某年
的网易的面试题还用过这个方法。
关于欧几里得和素因子分解在我的另一篇博文中:
http://blog.csdn.net/cassiepython/article/details/43154103