[最大公约数] 问题列表#1574 #1181

最大公约数的求法

最大公约数——辗转相除法(欧几里得算法)

给定两个数 a,b 求最大公约数。使用辗转相除法思路如下:

  1. 对于已知的两个自然数 a,b 假设 a>b。

  2. 计算 a/b 将得到的余数记为 r

  3. 如果 r=0 ,则 b 为求得的最大公约数,否则执行下一步

  4. 将 b 的值保存到 a 中,将 r 的值保存到 b 中,重复执行(2)(3),直到 r=0 ,便得到最大公约数。

将代码简化,扩大范围到 long long 则代码如下:

long long gcd(long long a,long long b)
{ 
    return !b ? a : gcd(b, a%b); 
}

最大公约数——Stein 算法

引言:

欧几里得算法简单的同时也具有很高的效率,但如果计算的数超过32位,往往需要反复试商,效率大大降低。

改进:

Stein 算法只有整数的移位和加减法,首先注意以下结论:gcd(a,a) = a,也就是一个数和他自身的公约数是其自身,gcd(ka,kb) = k gcd(a,b),也就是最大公约数运算和倍乘运算可以交换,特殊的,当k=2时,说明两个偶数的最大公约数必然能被2整除。

Stein算法其实是分类减小的思想,和欧几里得算法其实差不太多,主要是为了缩小那两个数,一共分三种情况:

  1. 两个都是偶数的情况,这个时候的做法是将这两个数都减半,例如现在要求6和8的最大公约数,那么其实求到3和4的最大公约数再乘上2就好了。

  2. 一个奇数一个偶数的情况,这种情况只将偶数减半就好,因为最大公约数肯定是奇数。

  3. 两个都是奇数的情况,这个时候有两个做法:一个是转为求 A-B 和 B(两个数中小的那个)的最大公约数。另一种做法是转为求 (A+B)/2, (A-B)/2 的最大公约数,其实都是可以的。

把乘2改写成左移一位,除以2改写成右移一位,这样就可以精简代码:

long long GCD(long long a, long long b)
{
    if (a == b) return a;
    if (a == 0) return b;
    if (b == 0) return a;
    long long k = 0;
    while ((a & 1) == 0 && (b & 1) == 0)
    {
        a = a >> 1;
        b = b >> 1;
        k++;
    }
    while ((a & 1) == 0) a = a >> 1;
    while ((b & 1) == 0) b = b >> 1;//转为两数都是奇数的情况
    return GCD(abs(a - b) >> 1, min(a, b) << k);
    //或者写为:return GCD((a+b)>>1,abs(a-b)>>1);
}

 以上就是全部的拓展内容啦,下面就去写题目吧!

 


《1574:韩信又点兵》

题意:

一支军队总人数不大于100万。让士兵按每排A1人排成纵队,发现最后余了1个士兵;每排A2人,也是多1个士兵;...;每排Am人,也是多一个士兵。请求出满足条件的最大数字。如果无解,则输出-1。

题解:

例如对于 a,b,c 三个数求最大解:

  1. 输入 a,b 求 a,b 的最小公倍数 m
  2. 输入 c ,求 c 和 m 的最小公倍数,得到 n
  3. 对 n 反复自加 n,直到大于或等于100w
  4. 取上一次的数据,加1后输出。

上板子:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
long long gcd(long long a, long long b)
{
    return !b ? a : gcd(b, a % b);
}
int main()
{
    int n;
    cin >> n;
    while(n--)//n组案例
    {
        ll m, a, b, c;//m个数字,ab为两个数,c为两数的最大公因数
        cin >> m >> a;//先读一个数a
        m -= 1;
        while (m > 0)//当还有数字未读
        {
            cin >> b;
            if (a < 1000000) {
                c = gcd(a, b);
                a = a * b / c;
            }
            m--;
        }
        if (a >= 1000000 || m < 0)cout << -1 << endl;
        else
        {
            ll u = a;
            while (a < 1000000)
            {
                a += u;
            }
            cout << a - u + 1 << endl;
        }
    }
    return 0;
}

《1181:公因数》

题意:

输入两个正整数m和n,输出它们所有的公因数。

题解:

例如对于 m,n 求所有公因数:

  1. 求出 m,n 的最大公因数 q
  2. 求在 [1,q] 范围内的所有公因数

各种思路:

  • 使用 set 存储所有的公因数(可以自动排序,最后使用迭代器输出)
  • 使用 sqrt(q),缩小范围求公因数  //一个数对应一个数,例如对于 12,当判断1时同时存储 12/1,判断2时同时存储 12/2,判断到sqrt(12)为止。
  • 可以先输出较小的数(<=sqrt(q)),将较大的数存储后倒序输出。

 

制作:BDT20040

posted @ 2021-06-17 22:40  流白李  阅读(153)  评论(0编辑  收藏  举报