【洛谷P1002】题解

文章介绍了使用动态规划方法解决从起点到终点的最短路径问题,讨论了初始条件、状态转移方程以及如何处理马的控制点。通过C/C++代码演示了解决方案,并分析了时间复杂度和空间复杂度,提出了优化空间复杂度的可能性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

千里之行,始于足下,让我们从今天开始,走上通向神犇的道路!
同志们,加油!
任何一个伟大的思想,都有一个微不足道的开始。

声明:理解为重,代码次之!

原题

前言

真没想到洛谷主题库题目的难度涨得这么快啊 o r z \rm orz orz
这篇题解会讲述C/C++的做法(Python难一点就不会了😭)

思路分析

什么!!!标签竟有 动态规划,dp \colorbox{3498db}{\color{FFFFFF}动态规划,dp} 动态规划,dp!!!这么难!!!

仔细一看,其实这是一道小学奥数题,就是在许多交叉的线中找到到达终点的最短路线。老师可能教过例如“对角相连”的方法,但是这到底是为什么呢?让我们继续往下看。

思考最简单的方式,如果枚举往右或往下然后回溯搜索,就会超市,得到TLE。先考虑一个简化版的问题:如果那个马不存在,从左上角到右下角一共有多少种走法?
定义一个二维数组 f f f,记录从原点走到坐标 ( i , j ) (i,j) (i,j) 的方法总数是 f i , j f_{i,j} fi,j。当卒从起点开始,笔直往右或者笔直往下,无论走多远都只有唯一的一种走法(因为一旦偏移就再也回不去了),所以当 k ⩾ 0 k\geqslant0 k0 时, f 0 , k = f k , 0 = 1 f_{0,k}=f_{k,0}=1 f0,k=fk,0=1,这就是递推的初始条件。如何到点 ( 1 , 1 ) (1,1) (1,1) 呢?要么是从点 ( 0 , 1 ) (0,1) (0,1) 往下走一格,要么是从点 ( 1 , 0 ) (1,0) (1,0) 往右走一格,因此到 ( 1 , 1 ) (1,1) (1,1) 的方案数量就是到 ( 0 , 1 ) (0,1) (0,1) 的数量加上到 ( 1 , 0 ) (1,0) (1,0) 的数量,可得 f 1 , 1 = f 0 , 1 + f 1 , 0 f_{1,1} = f_{0,1} + f_{1,0} f1,1=f0,1+f1,0。可以归纳得到状态转移方程(递推式): f i , j = f i − 1 , j + f i , j − 1 ( i > 0 , j > 0 ) f_{i,j}=f_{i-1,j}+f_{i,j-1}(i>0,j>0) fi,j=fi1,j+fi,j1(i>0,j>0)
有了状态转移方程,有了初始条件,就可以求出完整的没有马的 f f f 数组的值了。

如果有些点因为马的把守而不能走呢?其实也没有什么区别,只不过没办法从马的控制点转移到下一个点罢了(换句话说,马的控制点上路径数全部清空)。此外,初始条件和递推范围也都一点变化,只需要 f 0 , 0 = 1 f_{0,0}=1 f0,0=1 即可,同时递推范围就是 i ⩾ 0 , j ⩾ 0 , i ⋅ j ≠ 0 i\geqslant0,j\geqslant0,i\cdot j\not=0 i0,j0,ij=0

代码演示

因为CC++大体实现相同,所以不再写两份代码偷懒

#include <iostream>
#define MAXN 22 // C++中推荐使用:const int MAXN = 22;
using namespace std;
long long f[MAXN][MAXN] = {0};
int ctr[MAXN][MAXN], n, m, hx, hy;
int d[9][2] = {{0, 0}, {1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, 1}, {-2, -1}};
// 马的控制范围相对于马的位置的偏移量
int main() {
	cin >> n >> m >> hx >> hy; // C中用scanf("%d%d%d%d", &n, &m, &hx, &hy);
	for (int i = 0; i < 9; i++) {
		int tmpx = hx + d[i][0], tmpy = hy + d[i][1];
		if (tmpx >= 0 && tmpx <= n && tmpy >= 0 && tmpy <= m) ctr[tmpx][tmpy] = 1;
		// 判断是否在棋盘范围内并记录马的控制点
	}
	f[0][0] = 1 - ctr[0][0]; // 如果原点就是马的控制点,则初始路径就是0,否则是1
	for (int i = 0; i <= n; i++)
		for (int j = 0; j <= m; j++) {
		if (ctr[i][j]) continue; // 若这个点是控制点,则跳过
		if (i) f[i][j] += f[i - 1][j]; // 若不在横轴上就加上上面路径数
		if (j) f[i][j] += f[i][j - 1]; // 若不在纵轴上就加上左边路径数
	}
	cout << f[n][m]; // 输出答案
	return 0;
}

复杂度

时间复杂度

很明显,程序中有一个 2 2 2 层循环,每个循环都是 0 ∼ n 0 \sim n 0n 0 ∼ m 0 \sim m 0m,所以最终时间复杂度是 O ( n m ) O(nm) O(nm),可以在给定时间内通过。

空间复杂度

定义的最大的一个数组是 f f f,可以看出空间复杂度是 O ( M A X N ⋅ M A X N ) O(MAXN\cdot MAXN) O(MAXNMAXN),也是 O ( n m ) O(nm) O(nm)

优化

当然,空间复杂度是可以优化成 O ( n ) O(n) O(n) 的,但这可以说是玄学且没有用,感兴趣的读者可以自己膜拜大神

总结

学习递推开始必刷的题目,NOIP考题,稍有思维难度。

<think>我们正在处理洛谷P2118题目,题目名为“比例简化”。根据题目,我们需要在给定范围内找到一个最简分数,使其与给定比例A/B的误差最小。具体来说,题目要求我们在[1, L]范围内找到两个整数A'和B',使得它们互质,且A'/B' >= A/B,同时使A'/B' - A/B尽可能小。如果有多个解,输出A'最小的;如果仍有多个,输出B'最小的。 解题思路: 1. 题目要求A'和B'在[1, L]范围内,且互质(即最大公约数为1)。 2. 我们需要满足A'/B' >= A/B,然后计算A'/B'与A/B的差值,并最小化这个差值。 3. 由于L的范围是1到100,我们可以枚举所有可能的A'和B'(1<=A',B'<=L),然后检查是否满足条件。 4. 条件包括:A'和B'互质,且A'*B >= B'*A(避免浮点数比较,可以转化为乘法比较:A'/B'>=A/B 等价于 A'*B>=B'*A)。 5. 在满足条件的分数中,我们选择差值最小的(即(A'*B - B'*A)最小,因为分母相同,分子越小差值越小?注意:这里差值实际上是(A'/B' - A/B),但为了精确比较,我们可以用交叉相乘后的分子差来比较,即(A'*B - B'*A)最小,因为分母B'*B是正数,所以比较分子差即可。但注意,当分子差相同时,题目要求先取A'小的,再取B'小的。 6. 但是注意:题目要求的是最简分数(即互质),所以我们在枚举时,如果两个数不互质,可以跳过。 然而,我们也可以优化:枚举分母B'(1到L),然后对于每个B',我们可以确定A'的下界:由于A'/B'>=A/B,所以A'>=ceil(B' * A / B)。同时A'不能超过L。然后在这个范围内枚举A',并检查是否互质。 但是,由于L很小(最大100),所以直接双重循环枚举A'和B'也是可行的。 步骤: 初始化: A, B, L 由输入给出。 min_diff = 一个很大的数(比如1e18),用于记录最小的分子差(即A'*B - B'*A)。 min_A, min_B 用于记录答案。 枚举A'从1到L,枚举B'从1到L: 如果gcd(A', B') != 1,跳过。 计算分子差:diff = A' * B - B' * A (注意,这里要求diff>=0,因为要满足A'/B'>=A/B) 如果diff<0,则这个分数不满足条件,跳过。 然后比较: 如果diff < min_diff,则更新min_diff, min_A=A', min_B=B' 如果diff==min_diff,那么比较A':如果当前A'小于min_A,则更新;如果A'相同,则比较B',取B'小的。 注意:由于题目要求A'/B'>=A/B,所以diff>=0是必须的。 但是,有可能没有解?题目中L>=1,且A,B是正整数,所以至少有一个解?因为A'=A, B'=B(如果A<=L且B<=L)是满足条件的,但题目要求A'和B'在[1,L]范围内,且互质。但注意,A和B本身可能不互质,所以不能直接取A'=A, B'=B。但我们可以枚举,所以一定有解吗?不一定,比如A=1, B=1, L=1,那么A'=1,B'=1是解。如果A=2, B=1, L=1,那么A'只能取1,B'取1,但是2/1>1/1,所以A'=1,B'=1满足1/1>=2/1?不满足,因为1<2。所以这里要注意:当A/B>L时,即A>B*L,那么不可能有A'<=L且A'/B'>=A/B(因为A'最大为L,B'最小为1,此时A'/B'最大为L,而A/B>L,所以没有分数满足条件)。但是题目中A和B的范围没有给出,但根据实际,A和B是正整数,且L>=1,所以我们需要考虑这种情况。 题目要求:如果无解,输出“No Solution”(但题目描述中并没有说无解的情况,但根据逻辑,有可能无解?)。我们再看题目描述:题目保证1<=A,B<=1000000,L<=100。所以有可能A/B>L(因为A最大1000000,B最小1,则A/B最大1000000,而L最大100,所以当A/B>L时,不可能有A'<=L且A'/B'>=A/B)。因此,我们需要考虑无解的情况。 所以,在枚举之前,我们可以先判断:如果A/B>L,那么无解(因为即使取A'=L, B'=1,则A'/B'=L,而A/B>L,所以不满足A'/B'>=A/B)。但是注意,我们枚举的条件是A'/B'>=A/B,所以当A/B>L时,确实无解。 但是,还有一种情况:即使A/B<=L,但可能没有互质的分数满足条件?实际上,因为分数值A/B是固定的,而我们可以取非最简分数?但题目要求互质。例如,A=2,B=2,那么A/B=1,那么我们可以取A'=1,B'=1(互质)满足条件。所以只要存在一个分数值大于等于A/B,并且该分数可以化简为互质的分数,那么就有解。但是,如果A/B>L,则无解。 因此,步骤: 1. 读入A, B, L。 2. 如果A * 1.0 / B > L,则输出“No Solution”并结束。 3. 否则,枚举A'和B'(1到L),并检查: 满足条件:A' * B >= B' * A (即A'/B'>=A/B) 且gcd(A',B')==1 4. 在满足条件的分数中选择使分子差(A'*B-B'*A)最小的A'和B',如果分子差相同,则按A'升序,再按B'升序。 注意:分子差相同的情况,我们按照题目要求:先取A'最小的,如果A'相同再取B'最小的。 但是,由于我们枚举了所有互质的分数,所以我们可以直接比较。 代码实现: 使用两层循环,然后利用欧几里得算法求最大公约数。 由于L最大100,所以循环次数为100*100=10000,完全可以接受。 注意:在比较分子差时,我们使用整数乘法,避免浮点误差。 但是,有可能出现多个分数具有相同的分子差,所以我们需要按照题目要求选择。 具体代码步骤: min_diff = 一个很大的整数(如0x3f3f3f3f或更大,因为A,B最大1000000,所以分子差最大可能达到100*1000000=10^8,所以min_diff初始值要大于10^8,比如设为1e18) minA = 0, minB = 0 for a in range(1, L+1): for b in range(1, L+1): if gcd(a,b) != 1: # 不互质,跳过 continue # 计算分子差:diff = a*B - b*A if a*B < b*A: # 不满足A'/B'>=A/B continue diff = a*B - b*A if diff < min_diff: min_diff = diff minA = a minB = b elif diff == min_diff: # 如果当前分子差等于最小分子差,则比较a和minA if a < minA: minA = a minB = b elif a == minA and b < minB: minB = b 如果minA和minB仍然是0,说明没有找到(但理论上应该有解,因为我们有无解判断?实际上,我们前面已经判断了A/B<=L,所以应该有解,但可能因为循环中没找到?但注意,我们循环中要求互质且满足分数条件,所以可能没有互质的分数满足条件?但是,由于分数值A/B<=L,那么至少存在一个分数:比如A'=ceil(A/B)且B'=1,但是ceil(A/B)可能大于L?不,因为A/B<=L,所以ceil(A/B)<=ceil(L)<=L(因为L是整数,所以ceil(L)=L)?不对,例如A/B=0.5,那么ceil(0.5)=1,而1<=L(因为L>=1)。所以一定有解吗?但是注意,我们要求A'/B'>=A/B,且A'和B'都是整数,那么取B'=1,A'=ceil(A/B)是满足的,但可能不互质?比如ceil(A/B)和1一定是互质的(因为gcd(x,1)=1)。所以一定有解。因此,我们不需要担心没有解的情况(在已经通过无解判断后)。 但是,我们初始设minA=0,如果循环后minA还是0,说明没有找到,那么应该输出无解?但理论上不会发生。为了保险,我们可以在循环结束后检查minA是否为0,如果是0则输出无解,否则输出minA和minB。 然而,我们前面已经判断了A/B<=L,所以一定存在解(比如取A'=ceil(A/B)且B'=1,这个分数满足条件且互质,所以会被枚举到)。因此,我们不需要再检查minA。 但是,注意:如果A/B是整数,那么ceil(A/B)=A/B,所以A'=A/B, B'=1。但是A/B可能不是整数,所以ceil(A/B)就是大于等于A/B的最小整数。因此,这个分数一定满足条件。 所以,代码实现如下: 注意:由于A,B,L都是整数,所以我们可以这样写。 但是,为了避免浮点数,我们可以这样判断:A/B<=L 等价于 A<=B*L(因为A,B,L>0)?是的,因为A/B<=L 等价于 A<=B*L(两边乘以正数B)。 因此,无解条件:如果A > B*L,则输出“No Solution”。 否则,我们进行枚举。 但是,还有一种情况:即使A<=B*L,但可能因为枚举过程中没有找到互质的分数?但是上面已经说明,至少存在一个解(A'=ceil(A/B), B'=1)且互质。所以不会出现没有解的情况。 因此,我们写代码。 另外,注意:题目要求输出A'和B',中间用空格隔开。 下面我们写代码,并注意使用欧几里得算法求最大公约数。 由于题目要求,我们使用C++编写。 但是,用户要求用中文回答,并且生成相关问题。我们先给出题解,然后生成相关问题。 代码示例: ```cpp #include <iostream> #include <algorithm> using namespace std; long long gcd(long long a, long long b) { if (b == 0) return a; return gcd(b, a % b); } int main() { long long A, B, L; cin >> A >> B >> L; // 判断无解:如果A > B*L if (A > B * L) { cout << "No Solution" << endl; return 0; } long long min_diff = 1e18; // 初始化一个很大的数 long long minA = 0, minB = 0; // 枚举A'和B' for (long long a = 1; a <= L; a++) { for (long long b = 1; b <= L; b++) { // 检查互质 if (gcd(a, b) != 1) continue; // 检查是否满足 a*B >= b*A if (a * B < b * A) continue; long long diff = a * B - b * A; // 更新最小值 if (diff < min_diff) { min_diff = diff; minA = a; minB = b; } else if (diff == min_diff) { // 如果差值相同,则按A'小的优先,再按B'小的 if (a < minA) { minA = a; minB = b; } else if (a == minA && b < minB) { minB = b; } } } } cout << minA << " " << minB << endl; return 0; } ``` 但是,这个程序在枚举时,如果L很大,双重循环可能会慢。但题目中L<=100,所以没问题。 另外,注意:我们使用long long是因为A,B最大1000000,乘积最大为1000000*100=10^8,所以用long long可以避免溢出(int最大约2e9,但10^8在int范围内?因为int通常为4字节,最大2^31-1约2e9,所以10^8在int范围内。但是,为了保险,我们使用long long,因为题目中A,B最大1000000,而L最大100,所以a最大100,b最大100,a*B最大100*1000000=10^8,在int范围内。但有些系统int可能只有2字节?所以用long long更安全)。 但是,题目没有给出A,B的范围,但引用中题目描述为1<=A,B<=1000000,L<=100,所以用long long更保险。 测试样例:题目中给出的样例 样例1:输入:1 1 1 -> 输出:1 1 样例2:输入:123 456 10 -> 输出:1 4 (因为1/4=0.25,而123/456≈0.2697,满足1/4<123/456?不,题目要求A'/B'>=A/B,所以1/4=0.25<0.2697,不满足条件。所以我们需要重新检查题目) 重新阅读题目:题目要求A'/B'>=A/B,但是样例2的输入123 456 10,输出1 4?不对,因为1/4=0.25小于123/456≈0.2697,所以不满足条件。那么正确的解是什么? 实际上,题目要求:在满足A'/B'>=A/B的前提下,使A'/B'与A/B的误差最小。那么比0.2697大的最小分数是多少?在分母不超过10的情况下,我们可以枚举: 分母b从1到10,分子a从ceil(b*A/B)到L(这里L=10,且a<=10): b=1: a>=ceil(123/456)=ceil(0.2697)=1,所以a=1,2,...,10 分数值:1/1=1.0 -> 差值=1-0.2697=0.7303 2/1=2.0 -> 差值更大 ... 10/1=10.0 -> 差值很大 b=2: a>=ceil(2*0.2697)=ceil(0.5394)=1 -> a=1,2,...,10 1/2=0.5 -> 0.5-0.2697=0.2303 2/2=1.0 -> 0.7303 ... b=3: a>=ceil(3*0.2697)=ceil(0.8091)=1 -> a=1,2,...,10 1/3≈0.3333 -> 0.3333-0.2697≈0.0636 2/3≈0.6667 -> 0.397 ... b=4: a>=ceil(4*0.2697)=ceil(1.0788)=2 -> a=2,3,...,10 2/4=0.5 -> 0.2303 3/4=0.75 -> 0.4803 ... b=5: a>=ceil(5*0.2697)=ceil(1.3485)=2 -> a=2,3,...,10 2/5=0.4 -> 0.1303 3/5=0.6 -> 0.3303 ... b=6: a>=ceil(6*0.2697)=ceil(1.6182)=2 -> a=2,...,10 2/6≈0.3333 -> 0.0636 (与1/3相同,但这里分母是6,分子2,不互质,所以我们在枚举中会跳过,因为要求互质) 3/6=0.5 -> 0.2303 (跳过,因为3/6不互质?但gcd(3,6)=3,跳过) 4/6≈0.6667 -> 跳过 5/6≈0.8333 -> 跳过 b=7: a>=ceil(7*0.2697)=ceil(1.8879)=2 -> a=2,...,10 2/7≈0.2857 -> 0.0160 (满足条件,且互质?gcd(2,7)=1,所以是候选) 3/7≈0.4286 -> 0.1589 ... b=8: a>=ceil(8*0.2697)=ceil(2.1576)=3 -> a=3,...,10 3/8=0.375 -> 0.1053 4/8=0.5 -> 跳过(不互质) 5/8=0.625 -> 跳过(不互质?5和8互质,但是5/8=0.625,差值0.3553,比2/7的0.0160大,所以不会更新) b=9: a>=ceil(9*0.2697)=ceil(2.4273)=3 -> a=3,...,10 3/9≈0.3333 -> 跳过(不互质) 4/9≈0.4444 -> 0.1747 ... b=10: a>=ceil(10*0.2697)=ceil(2.697)=3 -> a=3,...,10 3/10=0.3 -> 0.0303 4/10=0.4 -> 跳过 5/10=0.5 -> 跳过 ... 所以,最小的差值应该是0.0160(2/7),那么答案应该是2和7?但题目样例输出是1 4?这显然不对。 重新看题目:题目描述中样例输入输出: 输入:1498 902 10 输出:5 3 所以,我们之前对样例2的理解有误。题目中给出的样例输入是123 456 10,但输出并不是1 4。实际上,题目中并没有给出123 456 10的样例输出,是我们自己假设的。 所以,我们按照题目描述,应该用1498 902 10来测试。 计算1498/902≈1.66075,然后在[1,10]范围内找互质的A'和B',满足A'/B'>=1.66075,且使A'/B'尽可能接近1.66075。 枚举分母b(1到10): b=1: a>=ceil(1.66075)=2 -> a=2..10 2/1=2 -> 差值=2-1.66075=0.33925 3/1=3 -> 差值更大 ... b=2: a>=ceil(3.3215)=4 -> a=4..10 4/2=2 -> 差值0.33925(跳过,因为不互质) 5/2=2.5 -> 0.83925 ... b=3: a>=ceil(4.98225)=5 -> a=5..10 5/3≈1.6667 -> 差值=0.00595 6/3=2 -> 跳过(不互质) 7/3≈2.333 -> 差值大 ... b=4: a>=ceil(6.643)=7 -> a=7..10 7/4=1.75 -> 0.08925 ... b=5: a>=ceil(8.30375)=9 -> a=9..10 9/5=1.8 -> 0.13925 10/5=2 -> 跳过 b=6: a>=ceil(9.9645)=10 -> a=10 10/6≈1.6667 -> 但是gcd(10,6)=2,所以跳过。 b=7: a>=ceil(11.62525)=12 -> 但是a最大为10,所以跳过。 所以,最小的差值出现在5/3,差值0.00595,所以输出5 3。符合样例。 因此,我们的程序应该能够通过这个样例。 但是,为什么我们之前枚举123 456 10时,最小的分子差出现在哪里?我们计算一下分子差: 我们枚举到2/7时:分子差=2*456-7*123=912-861=51 而1/3:分子差=1*456-3*123=456-369=87 而2/5:分子差=2*456-5*123=912-615=297 而1/4:分子差=1*456-4*123=456-492=-36(不满足条件,因为小于0,所以被跳过) 所以,我们枚举2/7时,分子差51,那么有没有更小的? 我们枚举3/10:分子差=3*456-10*123=1368-1230=138 枚举1/1: 456-123=333 枚举3/7: 3*456-7*123=1368-861=507 枚举1/2: 456-2*123=456-246=210 枚举1/3: 456-3*123=456-369=87 枚举2/3: 2*456-3*123=912-369=543 枚举3/4: 3*456-4*123=1368-492=876 枚举4/5: 4*456-5*123=1824-615=1209 枚举5/7: 5*456-7*123=2280-861=1419 等等。 所以,在123456样例中,最小的分子差是51(2/7),所以输出2 7。 但是,为什么题目没有给出123456的样例输出?所以我们的程序应该输出2 7。 所以,我们的程序逻辑正确。 但是,注意:在枚举过程中,我们跳过了不互质的分数,所以不会出现非最简分数。 因此,我们按照上述代码实现即可。 相关问题: 1. 在洛谷P2118中,如果无解的条件是什么?如何判断? 2. 为什么在枚举过程中要跳过不互质的分数? 3. 除了枚举,还有更高效的解法吗? 4. 如何证明在满足A/B<=L的情况下,一定存在解? 5. 如果要求输出分数形式(即A'/B')的最简分数,但题目已经要求互质,所以直接输出即可。 我们生成相关问题: §§相关问题§§ 1. 洛谷P2118题目中无解的条件是什么?请详细说明。 2. 为什么在解题过程中需要保证A'和B'互质? 3. 有没有更高效的算法来解决洛谷P2118问题(比例简化)? 4. 如果L的范围扩大到1000,如何优化双重循环枚举? 5. 如何用数学方法推导出最优解的可能范围,从而减少枚举量?</think>### 洛谷 P2118 比例简化 题解 #### 题目分析 题目要求在范围 $[1, L]$ 内找到两个**互质**的整数 $A'$ 和 $B'$,满足: 1. $\frac{A'}{B'} \geq \frac{A}{B}$ 2. 差值 $\frac{A'}{B'} - \frac{A}{B}$ 最小化 3. 若差值相同,则取 $A'$ 最小的组合;若 $A'$ 相同,则取 $B'$ 最小的组合 **无解条件**:当 $\frac{A}{B} > L$ 时无解(即 $A > B \times L$),因为此时不存在满足 $\frac{A'}{B'} \geq \frac{A}{B}$ 的分数(即使取 $B'=1,\ A'=L$,仍有 $L < \frac{A}{B}$)[^1]。 #### 解题思路 1. **无解判断**:若 $A > B \times L$,直接输出 `No Solution`。 2. **双重枚举**:枚举所有可能的 $A'$(1 到 $L$)和 $B'$(1 到 $L$): - 跳过非互质对($\gcd(A', B') \neq 1$) - 跳过不满足 $\frac{A'}{B'} \geq \frac{A}{B}$ 的组合(等价于 $A' \times B < B' \times A$) 3. **比较分子差**:对有效组合计算分子差 $diff = A' \times B - B' \times A$(等价于比较分数差值) 4. **选择最优解**: - 优先选择 $diff$ 最小的组合 - 若 $diff$ 相同,选 $A'$ 最小的 - 若 $A'$ 相同,选 $B'$ 最小的 > **关键点**:使用整数乘法避免浮点误差,通过 $\gcd$ 确保互质,利用分子差简化比较过程[^2]。 #### 代码实现 ```cpp #include <iostream> #include <algorithm> using namespace std; long long gcd(long long a, long long b) { return b ? gcd(b, a % b) : a; } int main() { long long A, B, L; cin >> A >> B >> L; // 无解判断:A/B > L if (A > B * L) { cout << "No Solution" << endl; return 0; } long long min_diff = 1e18; // 初始化最小分子差 long long ansA = 0, ansB = 0; for (long long a = 1; a <= L; a++) { for (long long b = 1; b <= L; b++) { // 跳过非互质对 if (gcd(a, b) != 1) continue; // 检查是否满足 A'/B' ≥ A/B if (a * B < b * A) continue; long long diff = a * B - b * A; // 计算分子差 // 更新最优解 if (diff < min_diff) { min_diff = diff; ansA = a; ansB = b; } // 分子差相同时按题目要求选择 else if (diff == min_diff) { if (a < ansA || (a == ansA && b < ansB)) { ansA = a; ansB = b; } } } } cout << ansA << " " << ansB << endl; return 0; } ``` #### 复杂度分析 - **时间复杂度**:$O(L^2 \log L)$ 双重循环枚举 $L^2$ 次,每次 $\gcd$ 计算复杂度为 $O(\log L)$。 - **空间复杂度**:$O(1)$ 仅使用常数级额外空间。 > **注**:由于 $L \leq 100$,总计算量约 $10^4$ 次,完全可行[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三日连珠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值