欧几里德算法和唯一分解定理

刘汝佳《入门经典》上提供了一道经典的题目:

除法表达式,在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;
}
        
View Code

然而这道题还可以利用唯一分解定理求解,而可惜的是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;
}
View Code

 

posted @ 2016-06-27 21:27  江南何采莲  阅读(276)  评论(0编辑  收藏  举报