欧几里德算法和唯一分解定理
刘汝佳《入门经典》上提供了一道经典的题目:
除法表达式,在NYOJ上可以找到原题,题号1013
描述 给出一个这样的除法表达式:X1/X2/X3/···/Xk,其中Xi是正整数。除法表达式应当按照从左到右的顺序求和,例如表达式1/2/1/2值为1/4。但是可以在表达式中嵌入括号以改变计算顺序,例如表达式(1/2)/(1/2)的值为1. 输入 首先输入一个N,表示有N组测试数据, 每组数据输入占一行,为一个除法 表 达式,输入保证合法。 使表达式的值为整数。k<=10000,Xi<=100000000. 输出 输出YES或NO 样例输入 1 1/2/1/2 样例输出 YES
- 首先经过推算可知x1恒为分子,x2恒为分母,而其余的数字都可以通过括号移到分子上去
- 那么就可以写成x1*x3*x4........*xk/x2
- 只要上面乘积能够整除x2就可以化整。
- 首先,可以使用欧几里德算法,对于每一个xi,x2除以xi与x2的最大公约数gcd
#include <iostream> #include <cstring> #include <cstdio> using namespace std; char ch[50010]; //注意数字最长9位,而且还会有/字符 int gcd(int x,int y) { if(x<y) swap(x,y); //这个判断可以节省一点时间 return y==0?x:gcd(y,x%y); } int main() { int T; for(scanf("%d",&T);T;T--) { scanf("%s",ch); int top=0,flag=0; int tmp1,tmp2,tmp; tmp=0;tmp2=0,tmp1=0;; for(int i=0,len=strlen(ch);i<len;i++) { if(ch[i]>='0'&&ch[i]<='9') { tmp=tmp*10+ch[i]-'0'; if(i==len-1) { if(top==1) { tmp2=tmp; tmp2/=gcd(tmp2,tmp1); } if(top==0) tmp1=tmp; if(top>1) tmp2/=gcd(tmp2,tmp); } } else { if(top==0) tmp1=tmp; if(top==1) { tmp2=tmp; tmp2/=gcd(tmp2,tmp1); } if(top>1) tmp2/=gcd(tmp2,tmp); top++; tmp=0; } if(tmp2==1) {flag=1;break;} } if(flag) puts("YES"); else puts("NO"); } return 0; }
然而这道题还可以利用唯一分解定理求解,而可惜的是10的8次方会有5761455个素数,考虑最坏情况,时间上难以承受
唯一分解定理:每一个自然数都可以分解成一个或者多个素数相乘的形式(n=p1^a1*p2^a2*.....*pm^am)
只要分别求得x2和所有分子的pi指数,如果对于某个pi指数x2大于所有分子的话,那就无法化整了
#include <iostream> #include <cstdio> #include <cstring> #include <vector> #include <map> #define SET(a) memset(a,0,sizeof(a)) using namespace std; const int Max=50010; char ch[Max]; int cnt1[100000000],cnt2[100000000]; void take(int x) { int flag=0;if(x==2) flag=1; for(int i=2;i<=x;i++) { if(x%i==0) { while(x%i==0) { x/=i; cnt1[i]++; } } if(x==1) break; } } int main() { int T; for(scanf("%d",&T);T;T--) { scanf("%s",ch); int tmp=0,top=0,tmp2=0; SET(cnt1);SET(cnt2); for(int i=0,len=strlen(ch);i<len;i++) { if(ch[i]>='0'&&ch[i]<='9') { tmp=tmp*10+ch[i]-'0'; if(i==len-1) { take(tmp); } } else { if(top==1) tmp2=tmp; if(top!=1) take(tmp); tmp=0; top++; } } int flag=0; for(int i=2;i<=tmp2;i++) { if(tmp2%i==0) { while(tmp2%i==0) { cnt2[i]++; tmp2/=i; } } if(cnt2[i]>cnt1[i]) {flag=1;break;} if(tmp2==1) break; } if(flag) puts("NO"); else puts("YES"); } return 0; }