第十一天:不定方程求解

每日一道C++题:不定方程求解

问题:给定正整数a,b,c。求不定方程 ax+by=c 关于未知数x和y的所有非负整数解组数。
要求:输入一行,包含三个正整数a,b,c,两个整数之间用单个空格开,每个数均不大于1000。输出一个整数,即不定方程的非负整数解组数。

求解不定方程的核心思路------枚举与优化

  • 暴力枚举的原始思路:
    因为 x 和 y 都是非负整数,所以对于 x 来说,它的取值范围可以初步确定为 0 =<x =<c / a (当 y = 0 时, x 能取到的最大值 );同理, y 的取值范围是 0 =<y =< c / b (当 x = 0 时 )。最直接的做法就是遍历 x 所有可能的取值,然后对于每个 x ,计算 c - ax 看是否能被 b 整除,且得到的 y = (c - ax) / b 也是非负整数。

例如题目中的输入样例 2 3 18 , a = 2 , b = 3 , c = 18 。 x 的可能取值是 0 到 18/2 = 9 。当 x = 0 时, 18 - 20 = 18 , 18 / 3 = 6 , y = 6 是整数且非负,这是一个解;当 x = 3 时, 18 - 23 = 12 , 12 / 3 = 4 , y = 4 有效,以此类推,遍历完所有 x 后统计符合条件的解的个数。

  • 枚举的优化方向:
    上述暴力枚举在 a 很小(比如 a = 1 )且 c 很大(比如 c = 10^9 )时,会导致循环次数极大,效率极低。所以后续我们可以结合数论知识(像贝祖定理、通解公式 )来优化,减少不必要的枚举。对于 x ,其可能的最大值为 c // a (当 y = 0 时);对于 y ,其可能的最大值为 c // b (当 x = 0 时)。这样可以减少不必要的枚举。
cpp 复制代码
#include <iostream>
using namespace std;

int main() {
    int a, b, c;
    cin >> a >> b >> c;
    
    int count = 0;
    // 枚举x的可能取值,x是非负整数,最大可能为c/a(当y=0时)
    for (int x = 0; x <= c / a; ++x) {
        // 计算剩余需要由by满足的值
        int remainder = c - a * x;
        // 检查remainder是否能被b整除,且y是非负整数
        if (remainder >= 0 && remainder % b == 0) {
            int y = remainder / b;
            // 确保y是非负整数
            if (y >= 0) {
                ++count;
            }
        }
    }
    
    cout << count << endl;
    return 0;
}

通过 for 循环枚举 x 的可能取值,范围是从 0 到 c // a (确保 ax 不超过 c )。对于每个 x ,计算剩余需要满足的值 remainder = c - a * x 。检查 y 的合法性:如果 remainder 是非负的,并且能被 b 整除,则计算对应的 y 值,并检查 y 是否为非负整数。统计解的个数:如果找到合法的 y ,则增加解的计数器 count 。

问题拓展

  1. 贝祖定理

核心结论:

不定方程 ax + by = c 有解的充要条件是 gcd(a, b) |c (即 c 是 a 和 b 最大公约数的倍数 )。

在原问题基础上,先判断是否有解,再输出解的个数。

cpp 复制代码
#include <iostream>
using namespace std;

// 欧几里得算法求最大公约数
int gcd(int a, int b) {
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

int main() {
    int a, b, c;
    cin >> a >> b >> c;
    
    int g = gcd(a, b);
    // 先判断是否有解
    if (c % g != 0) {
        cout << 0 << endl; // 无解
        return 0;
    }
    
    // 有解时,转化为等价方程
    a /= g;
    b /= g;
    c /= g;
    
    int count = 0;
    // 枚举x的可能取值,注意范围变化
    for (int x = 0; x <= c / a; ++x) {
        int remainder = c - a * x;
        if (remainder >= 0 && remainder % b == 0) {
            int y = remainder / b;
            if (y >= 0) {
                ++count;
            }
        }
    }
    
    cout << count << endl;
    return 0;
}
  • 让代码更严谨,避免无效枚举(比如输入 a=2, b=4, c=5 时直接判断无解 )。
  1. 通解公式与解的结构
  • 不定方程的通解可表示为:

    若 (x0, y0) 是一组特解,则所有解为:

    x = x0 + b/gcd(a,b) *t

    y = y0 - a/gcd(a,b) * t

    ( t 为整数,需保证 x >=0, y >=0 )

  • 扩展欧几里得算法不仅能求最大公约数,还能找到满足 ax + by = gcd(a,b) 的一组整数解 (x0, y0) 。当原方程 ax + by = c 有解时(即 gcd(a,b) | c ),我们可以先将方程两边除以 gcd(a,b) 得到等价方程,再利用扩展欧几里得算法找到特解,然后通过乘以相应系数得到原方程的特解。

  • 扩展题目:输出所有非负整数解的具体形式(不止个数,还要列出 (x,y) 对 )。

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int gcd(int a, int b) {
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

// 扩展欧几里得算法求特解
void extendedGcd(int a, int b, int& g, int& x0, int& y0) {
    if (b == 0) {
        g = a;
        x0 = 1;
        y0 = 0;
    } else {
        extendedGcd(b, a % b, g, y0, x0);
        y0 -= (a / b) * x0;
    }
}

int main() {
    int a, b, c;
    cin >> a >> b >> c;
    
    int g = gcd(a, b);
    if (c % g != 0) {
        cout << 0 << endl;
        return 0;
    }
    
    a /= g;
    b /= g;
    c /= g;
    
    int x0, y0;
    extendedGcd(a, b, g, x0, y0); // 求特解
    
    // 特解调整到非负(通解的基础)
    x0 *= c;
    y0 *= c;
    
    vector<pair<int, int>> solutions;
    
    // 通解参数t的范围:保证x>=0, y>=0
    int t_min, t_max;
    // x = x0 + b*t >= 0
    t_min = ceil((-x0) / (double)b);
    // y = y0 - a*t >= 0
    t_max = floor(y0 / (double)a);
    
    for (int t = t_min; t <= t_max; ++t) {
        int x = x0 + b * t;
        int y = y0 - a * t;
        if (x >= 0 && y >= 0) {
            solutions.emplace_back(x, y);
        }
    }
    
    // 输出解的个数和具体解
    cout << solutions.size() << endl;
    for (auto& p : solutions) {
        cout << "(" << p.first << ", " << p.second << ")" << endl;
    }
    
    return 0;
}
  • 从"计数"升级到"找所有解",理解不定方程解的结构。
  1. 数学推导优化

利用通解公式,直接计算 t 的范围,避免枚举 x :

  • 用扩展欧几里得找到特解 (x_0, y_0) 。
  • 推导 t 的取值范围,使得 x \geq 0, y \geq 0 。
  • 解的个数 = t_{\text{max}} - t_{\text{min}} + 1 (若有解 )。
cpp 复制代码
#include <iostream>
#include <cmath>
using namespace std;

int gcd(int a, int b) {
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

void extendedGcd(int a, int b, int& g, int& x0, int& y0) {
    if (b == 0) {
        g = a;
        x0 = 1;
        y0 = 0;
    } else {
        extendedGcd(b, a % b, g, y0, x0);
        y0 -= (a / b) * x0;
    }
}

int main() {
    int a, b, c;
    cin >> a >> b >> c;
    
    int g = gcd(a, b);
    if (c % g != 0) {
        cout << 0 << endl;
        return 0;
    }
    
    a /= g;
    b /= g;
    c /= g;
    
    int x0, y0;
    extendedGcd(a, b, g, x0, y0);
    x0 *= c;
    y0 *= c;
    
    // 计算t的范围
    // x = x0 + b*t >= 0 --> t >= ceil( (-x0)/b )
    // y = y0 - a*t >= 0 --> t <= floor( y0/a )
    int t_min = ceil( (-x0) / (double)b );
    int t_max = floor( y0 / (double)a );
    
    // 解的个数
    int count = 0;
    if (t_min <= t_max) {
        count = t_max - t_min + 1;
    }
    
    cout << count << endl;
    return 0;
}

实际场景迁移:资源分配问题

  1. 场景 1:货币兑换

问题描述:

用面值为 a 和 b 的硬币,凑出金额 c ,有多少种非负整数组合?

对应模型:

ax + by = c 的非负整数解个数,直接对应本题。

  1. 场景 2:任务调度

问题描述:

机器 A 每小时处理 a 个任务,机器 B 每小时处理 b 个任务,要处理 c 个任务,有多少种非负整数时间分配方案?

对应模型:

ax + by = c ,其中 x 是机器 A 的工作时间, y 是机器 B 的工作时间。

相关推荐
恒者走天下17 分钟前
cpp c++面试常考算法题汇总
c++
2301_809561521 小时前
c++day5
java·c++·面试
晨非辰1 小时前
#C语言——学习攻略:深挖指针路线(四续)——函数指针数组--转移表
c语言·开发语言·经验分享·学习·visual studio
学习编程的gas1 小时前
C++:STL中list的使用和模拟实现
开发语言·c++
shanks662 小时前
【图像处理】直方图均衡化c++实现
c++·图像处理·数码相机
lsnm2 小时前
【LINUX网络】使用TCP简易通信
linux·服务器·c语言·网络·c++·tcpdump
今晚打老虎2 小时前
c++之基础B(第一课)
开发语言·c++
hz_zhangrl2 小时前
CCF-GESP 等级考试 2025年6月认证C++七级真题解析
c++·程序设计·gesp·gesp2025年6月·c++七级·c++七级解析
乌萨奇也要立志学C++3 小时前
【C++详解】深入解析多态 虚函数、虚函数重写、纯虚函数和抽象类、多态原理、重载/重写/隐藏的对⽐
c++
NuyoahC3 小时前
笔试——Day23
c++·算法·模拟