涉及知识点

二分查找

题目

一个正整数如果能被 a 或 b 整除,那么它是神奇的。
给定三个整数 n , a , b ,返回第 n 个神奇的数字。因为答案可能很大,所以返回答案 对 109 + 7 取模 后的值。
示例 1:
输入:n = 1, a = 2, b = 3
输出:2
示例 2:
输入:n = 4, a = 2, b = 3
输出:6
提示:
1 <= n <= 109
2 <= a, b <= 4 * 104

分析

令f(x)等于[1,x]神奇数字的数量,寻找第一个f(x)大于等于n的x,用左开右闭的二分查找。结果一定在[1,max(a,b)*n]中。

神奇数字数量

神奇数字数量等于= 被a整除+ 被b整除 - 同时被a和b整除
同时被a和b整除:被a和b的最小公倍数整除,不是被a*b整除

代码

核心代码

int GCD(int n1, int n2)
 {
 int t1 = min(n1, n2);
 int t2 = max(n1, n2);
 if (0 == t1)
 {
 return t2;
 }
 return GCD(t2 % t1, t1);
 }class Solution {
 public:
 int nthMagicalNumber(int n, int a, int b) {
 long long left = -1, right = max(a, b) * (long long)n ;
 while (right - left > 1)
 {
 const auto mid = left + (right - left) / 2;
 const long long llNum = mid / a + mid / b - mid /( a * b/GCD(a,b));
 if( llNum >= n )
 {
 right = mid; 
 }
 else
 {
 left = mid;
 }
 }
 return right%(100010001000+7);
 } 
 };

测试用例

template
 void Assert(const T& t1, const T& t2)
 {
 assert(t1 == t2);
 }template
 void Assert(const vector& v1, const vector& v2)
 {
 if (v1.size() != v2.size())
 {
 assert(false);
 return;
 }
 for (int i = 0; i < v1.size(); i++)
 {
 Assert(v1[i], v2[i]);
 }
 }int main()
 {
 vector<vector> grid;
 int res = 0;
 {
 Solution slu;
 res = slu.nthMagicalNumber(5, 2, 4);
 Assert(10, res);
 }
 {
 Solution slu;
 res = slu.nthMagicalNumber(3, 4, 1000);
 Assert(12, res);
 }
 {
 Solution slu; 
 res = slu.nthMagicalNumber(4, 2, 3);
 Assert(6, res);
 }//CConsole::Out(res);}

左闭右开也可以,就是复杂些。

一,先找到最后一个f(x)<=n的x。
二,max(x-x%a,x-x%b);
比如: n =3 a =2 b =3 f(5)是最后一个等于3的x,结果是4。

class Solution {
 public:
 int nthMagicalNumber(int n, int a, int b) {
 long long left = 0, right = max(a, b) * (long long)n+1 ;
 while (right - left > 1)
 {
 const auto mid = left + (right - left) / 2;
 const long long llNum = mid / a + mid / b - mid /( a * b/GCD(a,b));
 if( llNum <= n )
 {
 left = mid; 
 }
 else
 {
 right = mid;
 }
 }
 left = max(left - left % a, left - left % b);
 return left %(100010001000+7);
 } 
 };

2023年3月旧代码

class Solution {
 public:
 int nthMagicalNumber(int n, int a, int b) {
 const int iComMul = ab/GCD(a, b);
 long long left = -1, right = max(a, b)(long long)n;
 while (right > left + 1)
 {
 auto llMid = left + (right - left) / 2;
 long long llNum = llMid / a + llMid / b - llMid / iComMul;
 if (llNum >= n)
 {
 right = llMid;
 }
 else
 {
 left = llMid;
 }
 }
 return right % (1000 * 1000 * 1000 + 7);
 }
 };


相关下载

想高屋建瓴的学习算法,请下载《闻缺陷则喜算法册》doc版

洒家想对大家说的话

闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。

墨家名称的来源:有所得以墨记之。

如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境:

VS2022 C++17

C++二分查找算法的应用:第 N 个神奇数字_数学