数学问题小结
这几天,学习并完成了数学类的一些知识和题目,包括素数,期望,gcd等等。。。。。。进行一个简单的总结。
首先是素数。当题目中的数据量比较小时,我们可以用最简单的方法去一一搜素判断该数是否是一个素数。判断的方法则是让它与比他小的数一一作除法,若能够被整除,则是合数,否则是质数。为了降低时间复杂度,我们可以只判断2到这个数的平方根之间的数,因为能够被这个数整除的数一定小于等于这个数的平方根。代码如下:
1 int isprime(long long n) 2 { 3 long long i; 4 if (n==2) 5 { 6 return 1; 7 } 8 for (i=2;i<=sqrt(n);i++) 9 { 10 if (n%i==0) 11 { 12 return 0; 13 } 14 } 15 return 1; 16 }
用这种方法,虽然可以准确的找出素数,但如果题目中的数据量很大,我们就要考虑另外一种方法了,提前将所有的素数存放在一个地方,当我们判断的时候,只需要判断我们是否存放这个数即可。这样,我们就不用每次都进行判断,而是只需判断查找一次,大大节省了时间。那么,我们怎样寻找所有的素数呢,我们可以先假定所有的数都是质数,然后从2开始,将2的所有倍数都标记为合数,之后是3,以此类推。。。。。。。为了避免重复标记,节约时间,我们通常采用如下的快速筛素数的方法:若当前标记的是i的倍数,如果i是前面已经标记了的数的倍数,便不再进行标记。代码如下:
1 #include<stdio.h> 2 #include<bits/stdc++.h> 3 using namespace std; 4 const long long N=10000100; 5 int prime[N]={0},num_prime=0; 6 bool isNotPrime[N]={true,true}; 7 void panprime() 8 { 9 long long i,j; 10 for (i=2;i<N;i++) 11 { 12 if(!isNotPrime[i]) 13 { 14 prime[num_prime++]=i; 15 } 16 for (j=0;j<num_prime && i*prime[j]<N ;j++) 17 { 18 isNotPrime[i*prime[j]]=true; 19 if (!(i%prime[j])) 20 { 21 break; 22 } 23 24 } 25 } 26 }
当然,如果数据比较小,还是用第一种方法吧。说不定,用第一种方法所需的时间还比第二种小哦,因为找出所有的素数也是需要时间的。。。
结论:数据量比较小的时候选方法一,数据量比较大(需判断成千上万个数)时选方法二!
之后是GCD,用辗转相除法寻找两个数的最大公因数。是更好的选择,比传统的逐步寻找法快得多。
1 int gcd(int a,int b) 2 { 3 if (b==0) 4 { 5 return a; 6 } 7 gcd(b,a%b); 8 }
最后,上一道题吧:http://www.fjutacm.com/Problem.jsp?pid=2519
其实,这个题就是一个典型的找最大公因数的题,双方拥有的机会就是最大公因数到最大的数之间的个数。解决代码如下:
1 #include<stdio.h> 2 #include<bits/stdc++.h> 3 using namespace std; 4 long gcd(long a,long b) 5 { 6 if (b==0) 7 { 8 return a; 9 } 10 gcd(b,a%b); 11 } 12 int main() 13 { 14 long a[105]; 15 long n,i,j; 16 long maxA=0; 17 scanf("%ld",&n); 18 for (i=0;i<n;i++) 19 { 20 scanf("%ld",&a[i]); 21 maxA=max(maxA,a[i]); 22 } 24 25 long min=gcd(a[0],a[1]); 26 27 for(i=0;i<n;i++) 28 for (j=i+1;j<n;j++) 29 { 30 long ans; 31 32 ans=gcd(a[i],a[j]); 33 if (min>ans) 34 { 35 min=ans; 36 } 37 } 38 39 44 long key; 45 key=maxA/min; 46 key=key-n; 48 if (key%2==0) 49 { 50 printf("Bob\n"); 51 } 52 else 53 { 54 printf("Alice\n"); 55 } 56 57 return 0; 58 }
再来看看这个题:https://atcoder.jp/contests/abc182/tasks/abc182_c?lang=en
这个题的意思就是要寻找一个数,删除其中的几个数字后,得到的数是三的倍数。因为三的倍数的判定方法就是看这个数字的和是否是三的倍数。一个数字的和就只有3种情况:刚好是3的倍数,3的倍数余1,3的倍数余2.第一种情况不用处理,输出0就行。第二种情况要小心,它有两种情况,我就是少考虑了一种,而WA了几发:第一种是可以删除一个余数为1的数,第二种是删除2个余数为2的数,因为,两个余数为2的数的和余数的和为4,4对3取余正好是1。第三种情况则是删除一个余数为2的数字或者删除两个余数为1的数字。优先考虑前面的一种方案。至于统计余数为一和余数为二的数字,我们可以利用字符串来处理每一个数字,这样,既避免了数字非常大存不下情况的发生,又可以使数字的处理更加方便快捷,代码如下:
1 #include<stdio.h> 2 #include<string.h> 3 int main() 4 { 5 char n[20]; 6 gets(n); 7 8 int nn=strlen(n); 9 int s=0; 10 int ss[4]={0},i; 11 for (i=0;i<strlen(n);i++) 12 { 13 s=s+n[i]-48; 14 15 if ((n[i]-48)%3==1) 16 { 17 ss[1]++; 18 } 19 if ((n[i]-48)%3==2) 20 { 21 ss[2]++; 22 } 23 } 24 25 if (s%3==0) 26 { 27 printf("0"); 28 return 0; 29 } 30 else 31 { 32 s=s%3; 33 if (s==1) 34 { 35 36 if (ss[1]>=1 && (nn-1)>0) 37 { 38 printf("1"); 39 return 0; 40 } 41 else if (ss[2]>=2 && (nn-2)>0) 42 { 43 printf("2"); 44 return 0; 45 } 46 else 47 { 48 printf("-1"); 49 } 50 } 51 if (s==2) 52 { 53 if (ss[2]>=1 && (nn-1)>0) 54 { 55 printf("1"); 56 return 0; 57 } 58 else if (ss[1]>=2 && (nn-2)>0) 59 { 60 printf("2"); 61 62 } 63 else 64 { 65 printf("-1"); 66 } 67 } 68 } 69 return 0; 70 71 }