zoukankan      html  css  js  c++  java
  • P1029 [NOIP2001 普及组] 最大公约数和最小公倍数问题

    题目传送门

    一、穷举p和q

    #include <bits/stdc++.h>
    
    using namespace std;
    
    //最大公约数
    int gcd(int x, int y) {
        return y ? gcd(y, x % y) : x;
    }
    
    //最小公倍数
    int lcm(int x, int y) {
        return y / gcd(x, y) * x; //注意顺序,防止乘法爆int
    }
    
    int cnt;
    int x, y;
    
    int main() {
        //y的范围1e6,双重循环妥妥的TLE
        cin >> x >> y;
        //双重循环,傻查法,最基本,最朴素
        for (int i = x; i <= y; i++)
            for (int j = x; j <= y; j++)
                if (gcd(i, j) == x && lcm(i, j) == y) cnt++;
        cout << cnt << endl;
        return 0;
    }
    

    此暴力算法,得分(60),有(4)个点(TLE)。看来需要优化一下。

    二、利用p计算q

    因为性质4知道:(gcd(p,q)*lcm(p,q)=p*q),所以已知最大公约和最小公倍,枚举(p),就可以通过上面的等式计算获得到(q),这比上面的枚举(p)(q)的双重循环要减少一层循环,用信息学的行话就是时间复杂度由(O(N^2))降为(O(N)),历史级别的进步啊!!

    #include <bits/stdc++.h>
    
    using namespace std;
    
    //最大公约数
    int gcd(int x, int y) {
        return y ? gcd(y, x % y) : x;
    }
    
    //最小公倍数
    int lcm(int x, int y) {
        return y / gcd(x, y) * x; //注意顺序,防止乘法爆int
    }
    
    int cnt;
    int x; //最大公约数
    int y; //最小公倍数
    
    int main() {
        //利用性质4:gcd(p,q)*lcm(p,q)=p*q
        //可以减少一维循环
        cin >> x >> y;
        for (int p = x; p <= y; p++) { //穷举进化法
            int q = x * y / p; //已知p,通过性质最大公约*最小公倍=两个数的乘积,
            // 可以计算出另一个数字,这样,就可以去掉一层循环,效率明显提升。
            // 应该满足如下的要求
            if (gcd(p, q) == x && lcm(p, q) == y) cnt++;
        }
        cout << cnt << endl;
        return 0;
    }
    

    到此,此题就已经(AC)了,可以说出题人还是比较良心,(≧▽≦)/ 但抓住一个好题,一定要一追到底,看看还能挖出哪些办法来。

    三、穷举lcm(p,q)的约数

    我们知道,(p)(q)一定是最小公倍数的约数!(这不是废话吗?)如果我们能枚举(lcm(p,q))的所有约数,就肯定能碰到(p,q)
    那怎么才能枚举(lcm(p,q))的所有约数呢?我们这里采用的是从(1)遍历到(sqrt{y})的方法,试试(y%k==0),如果是的话,表示(k)就是(y)的约数。这样做,就可以找到(y)中所有的小因子,对应的大因子除一下就是了。这两个其实是一对一对出现的,比如(3,60)(60,3)就是两个。需要正着判断一次,反着再判断一次。同时小心类似于(sqrt{25}=sqrt{5*5})这样的情况,去重一个。

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    
    /**
     测试数据:
     3 60
    
     答案:4
     */
    //最大公约数
    LL gcd(LL x, LL y) {
        return y ? gcd(y, x % y) : x;
    }
    
    //最小公倍数
    int lcm(int x, int y) {
        return y / gcd(x, y) * x; //注意顺序,防止乘法爆int
    }
    
    int cnt;
    LL x; //最大公约数
    LL y; //最小公倍数
    
    int main() {
        cin >> x >> y;
        //理论依据:gcd(p,q)*lcm(p,q)=p*q
        //枚举最小公倍数y的约数,这个约数可能是p,也可能是y/p
        for (LL k = 1; k * k <= y; k++)
            if (y % k == 0) {
                //可能小因子是p,也可能大因子是p,但大小因子别一样,那样会算重了。
                LL p = k;
                LL q = x * y / k; //q是靠计算出来的。q=x*y/p
                if (gcd(p, q) == x) cnt++;
    
                //p=y/k,q=x*y/(y/k)=k*x 两组不能重复,就是 y/k!=k
                if (k == y / k)continue;
    
                //p是大因子的可能性
                p = y / k;
                q = k * x;
                if (gcd(p, q) == x) cnt++;
            }
        cout << cnt << endl;
        return 0;
    }
    

    这个计算效率就很牛了,时间复杂度:(O(sqrt{y}))

    四、穷举gcd(p,q)的倍数

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    
    /**
     测试数据:
     3 60
    
     答案:4
     */
    //最大公约数
    LL gcd(LL x, LL y) {
        return y ? gcd(y, x % y) : x;
    }
    
    //最小公倍数
    int lcm(int x, int y) {
        return y / gcd(x, y) * x; //注意顺序,防止乘法爆int
    }
    
    int cnt;
    LL x; //最大公约数
    LL y; //最小公倍数
    
    int main() {
        cin >> x >> y;
        //理论依据:gcd(p,q)*lcm(p,q)=p*q
        //枚举最大公约数x的倍数
        for (LL p = x; p <= y; p += x) {
            LL q = x * y / p;
            if (gcd(p, q) == x && lcm(p, q) == y) cnt++;
        }
        cout << cnt << endl;
        return 0;
    }
    
  • 相关阅读:
    ES6-11学习笔记--正则表达式的扩展
    ES6-11学习笔记--字符串的扩展
    ES6-11学习笔记--Map
    ES6-11学习笔记--Set
    ES6-11学习笔记--Symbol
    final
    MySQL
    爬虫1
    laravel
    HTML学习
  • 原文地址:https://www.cnblogs.com/littlehb/p/15196071.html
Copyright © 2011-2022 走看看