CC++ & Algorithm

GCD与LCM的综合应用

极难8
语言版本:通用
概述:通过分蛋糕、铺地砖、周期相遇、分数化简等多个生活实例,展示最大公约数和最小公倍数在实际问题中的综合运用,并给出代码实现。

GCD与LCM的综合应用

生活中的例子

我们已经知道,最大公约数和最小公倍数就像一对好朋友,经常一起出现。下面的例子帮你更好地理解它们:

  1. 分蛋糕:小明有长48厘米、宽36厘米的矩形蛋糕,想切成大小相等的正方形小块,要求不能浪费,且正方形边长是整数厘米。最大正方形边长是多少?能切成多少块?这里边长就是48和36的最大公约数12厘米。块数 = (48/12)*(36/12) = 4×3=12块。

  2. 铺地砖:小红家卫生间长240厘米、宽180厘米,要铺正方形地砖,地砖边长是整数厘米,不能切割,且要用整块砖铺满。最大能用边长多少厘米的砖?最少需要多少块?

    • 边长是240和180的最大公约数60厘米。块数 = (240/60)×(180/60)=4×3=12块。
    • 如果改问“用边长60厘米的砖铺需要多少块”,这是除法问题;但如果问“至少用多少块砖(边长最大)”,就是求gcd。
  3. 钟表问题:甲钟每12分钟响一次铃,乙钟每18分钟响一次铃,丙钟每20分钟响一次铃。它们同时响铃后,多久再次同时响铃?这是求12、18、20的最小公倍数,为180分钟=3小时。

  4. 分数化简:将分数48/36化为最简分数,需要用分子和分母同时除以最大公约数12,得到4/3。

  5. 队伍排列:士兵排成每行8人或每行12人都能正好排完,至少有多少人?这是求8和12的最小公倍数24。如果要求人数在100到200之间,则取24的倍数中在这个范围的(即120、144、168、192)。

  6. 分糖果:有24颗草莓糖和36颗巧克力糖,想把它们混合分给小朋友,每个小朋友分到的两种糖数量相同且刚好分完,最多可以分给几个小朋友?

    • 小朋友人数就是24和36的最大公约数12。每个小朋友分到2颗草莓糖和3颗巧克力糖。
  7. 循环赛日程:两支球队分别每3天和每4天训练一次,今天两队同时训练,下一次同时训练是几天后?这是求3和4的最小公倍数12天后。

数学原理与公式回顾

  • gcd(a,b) = d,则a和b可以表示为 a = d * m, b = d * n,且gcd(m,n)=1。
  • lcm(a,b) = a * b / gcd(a,b) = d * m * n。
  • 对于多个数,gcd和lcm都可以两两逐步计算,但注意gcd满足结合律和交换律。

应用题型1:已知两个数的积与最大公约数,求最小公倍数

例如,两个数的积是216,最大公约数是6,那么最小公倍数 = 积 / gcd = 216/6=36。

应用题型2:已知最大公约数和最小公倍数,求原数

设两个数为a和b,已知gcd(a,b)=g,lcm(a,b)=l。则有 a * b = g * l。且可以设 a = g * x, b = g * y,其中x,y互质。那么 l = g * x * y,所以 x*y = l/g。然后枚举互质的x,y即可求出a,b。

生活例子:小明和小红的年龄都是整数,年龄的最大公约数是6,最小公倍数是36,且小明比小红大。求两人的年龄。

  • 设g=6, l=36,则 x*y = 36/6 = 6。互质的(x,y)对可能有(1,6)和(2,3)。若x=1,y=6则a=6,b=36;若x=2,y=3则a=12,b=18。因为小明年龄大,所以小明18岁,小红12岁。

应用题型3:求多个数的最大公约数和最小公倍数

比如求数组[12,18,24]的gcd和lcm。gcd(12,18)=6, gcd(6,24)=6;lcm(12,18)=36, lcm(36,24)=72,所以lcm=72。
注意:计算多个数的lcm时,不能直接abc/gcd(a,b,c),因为公式不直接推广。必须两两逐步计算。

应用题型4:分数运算与化简

在分数加减乘除中,常常需要通分和约分,通分需要求分母的最小公倍数,约分需要求分子分母的最大公约数。

常见错误与注意事项

  1. 混淆gcd和lcm的用途

    • 分物、切割、分组等“分成最大块”的问题通常用gcd;
    • 同时发生、周期相遇、找公倍数等“下一次同时”的问题通常用lcm。
    • 例如“用边长多少的正方形地砖铺满”是gcd,“至少多少人能排成每行8人或12人”是lcm。
  2. 计算lcm时溢出

    • 公式 lcm = a * b / gcd(a,b) 中,如果a和b很大,a*b可能超过int范围。
    • 应该先除后乘lcm = a / gcd(a,b) * b,这样中间结果更小。
  3. gcd函数参数为负数

    • 分数可能有负分子或负分母,计算gcd时应先取绝对值。代码中已用abs处理。
  4. 多个数lcm不能直接相乘再除

    • 例如求12、18、24的lcm,12*18*24 / gcd(12,18,24)得到的结果不对(因为lcm不是这个公式)。必须两两计算。
  5. 忘记化简分数

    • 分数加法后别忘了约分,否则结果不是最简形式。

C++完整代码实现

我们编写一个综合程序,包含以下功能:

  • 求两个数的gcd和lcm
  • 求数组的gcd和lcm
  • 分数化简(用结构体表示分数)
#include <iostream>
#include <vector>
#include <cstdlib> // 为了abs函数
using namespace std;

// 最大公约数(欧几里得算法)
int gcd(int a, int b) {
    while (b) {
        int t = a % b;  // 余数
        a = b;          // 更新a为原b
        b = t;          // 更新b为余数
    }
    return a;
}

// 最小公倍数:先除后乘防止溢出
int lcm(int a, int b) {
    return a / gcd(a, b) * b;
}

// 数组的最大公约数
int gcd_array(const vector<int> &arr) {
    int res = arr[0];  // 初始化为第一个元素
    for (int i = 1; i < arr.size(); i++) {
        res = gcd(res, arr[i]);  // 依次两两计算
    }
    return res;
}

// 数组的最小公倍数
int lcm_array(const vector<int> &arr) {
    int res = arr[0];  // 初始化为第一个元素
    for (int i = 1; i < arr.size(); i++) {
        res = lcm(res, arr[i]);  // 依次两两计算
    }
    return res;
}

// 分数结构体
struct Fraction {
    int num;  // 分子
    int den;  // 分母
};

// 分数化简
Fraction reduce_fraction(Fraction f) {
    int g = gcd(abs(f.num), abs(f.den));  // 取绝对值求gcd
    f.num /= g;  // 分子除以gcd
    f.den /= g;  // 分母除以gcd
    // 通常分母为正
    if (f.den < 0) {
        f.num = -f.num;
        f.den = -f.den;
    }
    return f;
}

// 分数加法
Fraction add_fraction(Fraction a, Fraction b) {
    // 通分:分母为lcm
    int common_den = lcm(a.den, b.den);  // 公分母
    int new_num = a.num * (common_den / a.den) + b.num * (common_den / b.den);
    Fraction result = {new_num, common_den};
    return reduce_fraction(result);  // 最后约分
}

int main() {
    // 1. 两个数的gcd和lcm
    int x = 48;   // 第一个数
    int y = 36;   // 第二个数
    cout << "gcd(" << x << "," << y << ") = " << gcd(x, y) << endl;
    cout << "lcm(" << x << "," << y << ") = " << lcm(x, y) << endl;

    // 2. 数组的gcd和lcm
    vector<int> numbers = {12, 18, 24};  // 三个数
    cout << "数组[12,18,24]的gcd = " << gcd_array(numbers) << endl;
    cout << "数组[12,18,24]的lcm = " << lcm_array(numbers) << endl;

    // 3. 分数化简
    Fraction frac = {48, 36};  // 待化简的分数
    Fraction reduced = reduce_fraction(frac);
    cout << "分数48/36化简为 " << reduced.num << "/" << reduced.den << endl;

    // 4. 分数加法
    Fraction a = {1, 6};   // 第一个分数 1/6
    Fraction b = {1, 8};   // 第二个分数 1/8
    Fraction sum = add_fraction(a, b);
    cout << "1/6 + 1/8 = " << sum.num << "/" << sum.den << endl;  // 7/24

    return 0;
}

注意:在计算gcd时,我们使用了abs是因为分数可能有负数,但gcd函数通常用于非负整数,所以先取绝对值。

Python完整代码实现

Python同样实现这些功能,更加简洁。

import math

def gcd(a, b):
    """欧几里得算法求最大公约数"""
    while b:
        a, b = b, a % b
    return a

def lcm(a, b):
    """最小公倍数(先除后乘)"""
    return a // gcd(a, b) * b

def gcd_array(arr):
    """数组的gcd"""
    res = arr[0]  # 第一个数
    for num in arr[1:]:
        res = gcd(res, num)  # 两两计算
    return res

def lcm_array(arr):
    """数组的lcm"""
    res = arr[0]  # 第一个数
    for num in arr[1:]:
        res = lcm(res, num)  # 两两计算
    return res

def reduce_fraction(num, den):
    """化简分数,返回(分子,分母)"""
    g = gcd(abs(num), abs(den))  # 取绝对值求gcd
    num //= g  # 分子除以gcd
    den //= g  # 分母除以gcd
    if den < 0:  # 分母调为正
        num = -num
        den = -den
    return num, den

def add_fraction(a_num, a_den, b_num, b_den):
    """分数加法,a_num/a_den + b_num/b_den"""
    common_den = lcm(a_den, b_den)  # 公分母
    new_num = a_num * (common_den // a_den) + b_num * (common_den // b_den)
    return reduce_fraction(new_num, common_den)

# 测试
x, y = 48, 36  # 两个示例数
print(f"gcd({x},{y}) = {gcd(x, y)}")
print(f"lcm({x},{y}) = {lcm(x, y)}")

numbers = [12, 18, 24]  # 数组
print(f"数组{numbers}的gcd = {gcd_array(numbers)}")
print(f"数组{numbers}的lcm = {lcm_array(numbers)}")

num, den = reduce_fraction(48, 36)
print(f"分数48/36化简为 {num}/{den}")

s_num, s_den = add_fraction(1, 6, 1, 8)
print(f"1/6 + 1/8 = {s_num}/{s_den}")

Python内置的math.gcd可以直接用,但这里手动实现以便理解。

总结要点

  1. 最大公约数和最小公倍数是解决很多实际问题的工具,如分物、铺地砖、周期事件、分数化简等。
  2. 核心公式:lcm(a,b) = a * b / gcd(a,b)。编程时应先除后乘防溢出。
  3. 多个数的gcd可以两两连续计算,lcm同理。
  4. 分数化简需要同时用到gcd;分数通分需要用到lcm。
  5. 在解题时,注意分析问题到底求gcd还是lcm,有时候需要两者结合。比如在已知两个数的积和gcd时,可以求出lcm;或者利用互质关系解未知数。

通过本文学习,你应该能够灵活运用最大公约数和最小公倍数解决实际编程问题,并为后续更复杂的数论算法打下坚实基础。

相关知识点指引

学完gcd和lcm后,可以继续探索以下内容:

  • 扩展欧几里得算法:不仅能求gcd,还能解出方程ax+by=gcd(a,b)的一组整数解,用于模逆元、同余方程。
  • 质因数分解:通过分解质因数可以直观计算gcd和lcm(取公共质因子的最小次数、最大次数)。
  • 同余与中国剩余定理:解决“物品总数除以某数余几”的问题,其中会用到lcm求公共周期。
  • 分数高精度运算:当分子分母非常大时,需要结合大整数和gcd来化简。
  • 编程竞赛常见题型:如“两个数的gcd和lcm互推”、“区间内有多少个数与某数互质”(利用gcd性质)。

希望这些知识能成为你数论入门的第一块基石!

例题精讲

1单选题

小明家客厅地面是长方形,长540厘米,宽360厘米。现在要铺上正方形地砖,要求地砖边长是整数厘米,且不能切割地砖,问最大边长是多少?

A90厘米
B180厘米
C120厘米
D60厘米
2单选题

甲每3天去一次图书馆,乙每5天去一次,丙每7天去一次。他们某天在图书馆相遇,请问下一次相遇至少是多少天后?

A105天
B70天
C35天
D15天
3判断题

对于任意两个正整数a和b,有GCD(a,b) × LCM(a,b) = a × b。

4填空题
以下函数使用辗转相除法求两个正整数的最大公约数和最小公倍数。请补充代码使函数完整。

def gcd_lcm(a, b):
    orig_a, orig_b = a, b
    while b:
        a, b = b, a % b
    gcd = a
    lcm = ___  # 计算最小公倍数
    return gcd, lcm
5填空题
以下函数用于将分数化简为最简形式,即分子分母除以它们的最大公约数。请补充代码。

def gcd(x, y):
    while y:
        x, y = y, x % y
    return x

def simplify(numerator, denominator):
    g = ___  # 求最大公约数
    return numerator // g, denominator // g