当前位置: 首页 > news >正文

第十一天:不定方程求解

每日一道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 时)。这样可以减少不必要的枚举。
#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 最大公约数的倍数 )。

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

#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) 对 )。

#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>=0int t_min, t_max;// x = x0 + b*t >= 0t_min = ceil((-x0) / (double)b);// y = y0 - a*t >= 0t_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 (若有解 )。
#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 的工作时间。

http://www.lryc.cn/news/603828.html

相关文章:

  • 镁金属接骨螺钉注册检测:骨科植入安全的科学基石
  • Rust基础-part8-模式匹配、常见集合
  • 亚马逊 Vine 计划:评论生态重构与合规运营策略
  • 学习笔记-中华心法问答系统的性能提升
  • 小孙学变频学习笔记(十二)机械特性的调整 机械特性的改善
  • 想要批量提取视频背景音乐?FFmpeg 和转换器都安排上
  • Day 25:异常处理
  • VTK开发笔记(一):VTK介绍,Qt5.9.3+VS2017x64+VTK8.2编译
  • Zynq SOC FPGA嵌入式裸机设计和开发教程自学笔记:GPIO扩展与中断控制技术,万字详解!!
  • 车载刷写架构 --- 整车刷写中为何增加了ECU 队列刷写策略?
  • 服务器分布式的作用都有什么?
  • Windows下基于 SenseVoice模型的本地语音转文字工具
  • Ubuntu25.04轻量虚拟机Multipass使用Shell脚本自动创建并启动不同版本Ubuntu并复制文件
  • 【支持Ubuntu22】Ambari3.0.0+Bigtop3.2.0——Step1—基础环境准备
  • GaussDB 约束的语法
  • 【LeetCode】前缀表相关算法
  • 服务器数据恢复—RAID上层部署的oracle数据库数据恢复案例
  • Node.js 是怎么一步步撼动PHP地位的
  • #C语言——学习攻略:深挖指针路线(三)--数组与指针的结合、冒泡排序
  • 云原生MySQL Operator开发实战(四):测试策略与生产部署
  • 什么情况下会出现数据库和缓存不一致的问题?
  • PowerShell脚本自动卸载SQL Server 2025和 SSMS
  • 传媒行业视频制作:物理服务器租用是隐藏的效率引擎
  • 基于Coze平台的自动化情报采集与处理引擎—实现小红书图文到飞书的端到端同步
  • MySQL数据库 mysql常用命令
  • 堆的理论知识
  • 青少年软件编程图形化Scratch等级考试试卷(一级)2025年6月
  • 人工智能赋能社会治理:深度解析与未来展望
  • 不靠海量数据,精准喂养大模型!上交Data Whisperer:免训练数据选择法,10%数据逼近全量效果
  • 光环云在2025WAIC联合发布“AI for SME 全球普惠发展倡议”