当前位置:文档之家› (2020年编辑)ACM必做50题的解题-数论

(2020年编辑)ACM必做50题的解题-数论

poj 1061 青蛙的约会这题用的是解线性同余方程的方法,之前没接触到过,搜索资料后看到一个人的博客里面讲的很好就拷贝过来了。

内容如下:对于方程:ax≡b(mod m),a,b,m都是整数,求解x 的值,这个就是线性同余方程。

符号说明:mod表示:取模运算ax≡b(mod m)表示:(ax - b) mod m = 0,即同余gcd(a,b)表示:a和b的最大公约数求解ax≡b(mod n)的原理:对于方程ax≡b(mod n),存在ax + by = gcd(a,b),x,y是整数。

而ax≡b(mod n)的解可以由x,y来堆砌。

具体做法如下:第一个问题:求解gcd(a,b)定理一:gcd(a,b) = gcd(b,a mod b)欧几里德算法int Euclid(int a,int b){if(b == 0)return a;elsereturn Euclid(b,mod(a,b));}附:取模运算int mod(int a,int b){if(a >= 0)return a % b;elsereturn a % b + b;}第二个问题:求解ax + by = gcd(a,b)定理二:ax + by = gcd(a,b)= gcd(b,a mod b) = b * x' + (a mod b) * y'= b * x' + (a - a / b * b) * y'= a * y' + b * (x' - a / b * y')= a * x + b * y则:x = y'y = x' - a / b * y'以上内容转自/redcastle/blog/item/934b232dbc40d336349bf7e4.html由这个可以得出扩展的欧几里德算法:int exGcd(int a, int b, int &x, int &y) {if(b == 0){x = 1;y = 0;return a;}int r = exGcd(b, a % b, x, y);int t = x;x = y;y = t - a / b * y;return r;}代码:#include<iostream>#include<cstdlib>#include<cstring>#include<cmath>using namespace std;__int64 mm,nn,xx,yy,l;__int64 c,d,x,y;__int64 modd(__int64 a, __int64 b){if(a>=0)return a%b;elsereturn a%b+b;}__int64 exGcd(__int64 a, __int64 b) {if(b==0){x=1;y=0;return a;}__int64 r=exGcd(b, a%b);__int64 t=x;x=y;y=t-a/b*y;return r;}int main(){scanf("%I64d %I64d %I64d %I64d %I64d",&xx,&yy,&mm,&nn,&l);if(mm>nn) //分情况{d=exGcd(mm-nn,l);c=yy-xx;}else{d=exGcd(nn-mm,l);c=xx-yy;}if(c%d != 0){printf("Impossible\n");return 0;}l=l/d;x=modd(x*c/d,l); ///取模函数要注意printf("%I64d\n",x);system("pause");return 0;}POJ 1142 SmithNumber题意:寻找最接近而且大于给定的数字的SmithNumber什么是SmithNumber?用sum(int)表示一个int的各位的和,那一个数i如果是SmithNumber,则sum(i) =sigma( sum(Pj )),Pj表示i的第j个质因数。

例如4937775= 3*5*5*65837,4+9+3+7+7+7+5 = 42,3+5+5+(6+5+8+3+7) = 42,所以4937775是SmithNumber。

思路:要寻找大于给定数字且最接近给定数字的SmithNumber,只要将给定数字不断的加1,判断它是否是SmithNumber就行了,如果是SmithNumber就立即输出。

但是如何判断是否是SmithNumber呢?首先就是要对数字进行质因数分解。

质因数分解要保证因子都是质数。

这里先介绍一个如何判断一个数int是否是质数呢,如果对于这个数,i = 2.....sqrt(int)都不是它的约数,那int就是一个质数。

所以我们可以将i初始化为2,然后不断递增i,看i是否是int的一个约数,如果是的话那i就是int的一个质因数(因为这个数是从2开始第一个可以整除int的数,它不可能是一个可以分解的合数,否则,它的约数在它之前就整除int),然后将int除以该质因数,重置i为2,重新对int判断它是否是质数。

这样最后剩下的int一定是一个质数,从而对int进行了质因数分解然后就很简单的将数字各质因数的各位加起来,看和是否等于该数字的各位和,如果相等那它可能就是SmithNumber,为什么说只是可能呢,因为这个数可能是质数,但是质数不是SmithNumber。

#include <stdio.h>#include <math.h>int Sum( int number ){int sum = 0;while( number != 0 ){sum += number % 10;number /= 10;}return sum;}bool SmithNumber( int number ){int i = 2;int temp = number;int sumOfNumber = Sum( number );int sum = 0;while( i <= (int)sqrt( (double)number ) ){if ( number % i ==0 ){sum += Sum( i );number /= i;i = 2;}else{++i;}//以上的代码做了无谓的计算,可用下面的代码,更新于20090904 //while( number % i == 0 )//{// sum += sum(i);// number /= i;//}// ++i;}sum += Sum( number );if ( sum == sumOfNumber && number != temp ){return true;}else{return false;}}int main(){while( true ){int num;scanf("%d",&num );if ( num == 0 ){break;while( true ){if ( SmithNumber(++num)){printf("%d\n", num);break;}}}return 0;}ACM——POJ 2262(Goldbach's Conjecture)题目地址:/JudgeOnline/problem?id=2262题目思路:对于任何一个偶数 n,从 x = 1 和 y = n - 1 开始,看 x、y 是否是质数,不是则 x += 2, y += 2这题需要开很大的内存,我 RE n 次,居然是因为数组开太小了,我这题耗时不是很理想,但我的耗内存在我看到的中是最小的,所以看来 OJ 上的题只要能开内存的就尽量开。

估计我这题用栈耗时了。

#include <iostream>#include <stdio.h>#include <math.h>#include <stack>#include <memory.h>#include <string.h>using namespace std;// 判断是否为质数的函数int IsPrime ( int x ){int i;if( x < 2 )return 0;for( i = 2; i <= (int) ( sqrt( (double)x + 0.5 ) ); i++ )if( x % i == 0)return 0;return 1;int main(){// 输入数,输入数 / 2 向上延伸,输入数 / 2 向下延伸,输入数 / 2int m_Input, m_Num_Max, m_Num_Min, m_InputToTwo;// 总体输出char m_Output[ 1000000 ];memset( m_Output, 0, 1000000 );// 标识 m_Output 的 Posint m_Output_Pos = 0;// 是否找到标识bool b_Find;// 栈stack<int> m_Stack;// 临时数int m_Value_Top;// 循环输入while ( scanf( "%d", &m_Input ) && ( m_Input != 0 ) ){b_Find = true;// m_Input 肯定是一个偶数m_InputToTwo = m_Input / 2;// 置值m_Num_Max = m_Input - 1;m_Num_Min = 1;// 寻找,直至都为素数或者找不到为止while ( ( !IsPrime( m_Num_Max ) ) || ( !IsPrime( m_Num_Min ) ) ){// 否则,前进 & 后退 2 格m_Num_Max -= 2;m_Num_Min += 2;// 如果发生如下情况,则输出 "Goldbach's conjecture is wrong."if ( ( m_Num_Max < m_InputToTwo ) || ( m_Num_Min > m_InputToTwo ) ){char* m_TempChar = "Goldbach's conjecture is wrong.\n"; strcat( m_Output, m_TempChar );b_Find = false;m_Output_Pos += strlen( m_TempChar );break;}}// 如果找到了if ( b_Find ){// 将 m_Input 转换为字符串存入 m_Outputwhile ( m_Input != 0 ){m_Value_Top = m_Input % 10;m_Stack.push( m_Value_Top );m_Input /= 10;}while ( !m_Stack.empty() ){m_Value_Top = m_Stack.top();m_Output[ m_Output_Pos++ ] = m_Value_Top + 48;m_Stack.pop();}// 加入 =m_Output[ m_Output_Pos++ ] = ' ';m_Output[ m_Output_Pos++ ] = '=';m_Output[ m_Output_Pos++ ] = ' ';// 将 m_Num_Min 转换为字符串存入 m_Outputwhile ( m_Num_Min != 0 ){m_Value_Top = m_Num_Min % 10;m_Stack.push( m_Value_Top );m_Num_Min /= 10;}while ( !m_Stack.empty() ){m_Value_Top = m_Stack.top();m_Output[ m_Output_Pos++ ] = m_Value_Top + 48;m_Stack.pop();}// 加入 +m_Output[ m_Output_Pos++ ] = ' ';m_Output[ m_Output_Pos++ ] = '+';m_Output[ m_Output_Pos++ ] = ' ';// 将 m_Num_Max 转换为字符串存入 m_Outputwhile ( m_Num_Max != 0 ){m_Value_Top = m_Num_Max % 10;m_Stack.push( m_Value_Top );m_Num_Max /= 10;}while ( !m_Stack.empty() ){m_Value_Top = m_Stack.top();m_Output[ m_Output_Pos++ ] = m_Value_Top + 48;m_Stack.pop();}// 加入 \nm_Output[ m_Output_Pos++ ] = '\n';}}// 输出printf( "%s", m_Output );system( "pause" );return 0;}POJ 2407 Relatives这题从题意可以看出就是求比从1~n - 1从有几个数和n没有公共因子,通常的算法很简单就能够想到,我开始也是按通常的做法写了一个,觉得可能会TLE, 果不其然,后来上网查了一下,知道了欧拉函数,这个就是求比n小的数中与n互质(也就是没有公共因子)的算法,看来还是那些经典的算法效率比较高,比纯用暴力试探高多了...欧拉函数描述如下:利用欧拉函数和它本身不同质因数的关系,用筛法计算出某个范围内所有数的欧拉函数值。

相关主题