信息学奥赛一本通 1966:【14NOIP普及组】比例简化 | 洛谷 P2118 [NOIP2014 普及组] 比例简化

本文介绍了如何解决比例简化的问题,主要涉及枚举方法和最大公约数的应用。通过枚举A'和B'的值,找到使A'/B'最接近A/B的比例,并确保A'与B'互质。两种解题思路分别包括使用最大公约数函数判断互质和不进行判断。代码示例展示了这两种方法的实现,帮助理解算法细节。

【题目链接】

ybt 1966:【14NOIP普及组】比例简化
洛谷 P2118 [NOIP2014 普及组] 比例简化

【题目考点】

1. 枚举

【解题思路】

解法1:枚举 保存浮点数 A B \frac{A}{B} BA

使用浮点型变量保存 A B \frac{A}{B} BA的值
观察L的范围,为1~100,因此可以使用枚举算法。

  • 枚举对象:i、j(i表示A’的值,j表示B’的值)
  • 枚举范围:i、j的枚举范围都是1~100
  • 判断条件:设 a n s ans ans保存已经出现过的满足大于等于d的最小的 i j \frac{i}{j} ji值。要想找到满足 A ′ B ′ ≥ A B \frac{A'}{B'}\ge \frac{A}{B} BABA的最小的 A ′ B ′ \frac{A'}{B'} BA,判断条件应为: i j ≥ A B ∧ i j < a n s \frac{i}{j} \ge \frac{A}{B}\land\frac{i}{j}<ans jiBAji<ans,该比较都是浮点型量之间的比较。
    满足条件时使用 r i , r j ri,rj ri,rj记录当前 i i i j j j的值。

对于A’与B’是否互质,可以在记录结果前使用gcd函数判断二者的最大公约数是否为1。
这一步不做其实也可以。对于 i j = i ′ j ′ \frac{i}{j}=\frac{i'}{j'} ji=ji i < i ′ ∧ j < j ′ i<i'\land j<j' i<ij<j的情况,由于i与j都是从小到大遍历的,一定是先遍历到i与j互质的情况,后遍历到二者不互质的情况。在判断 i j < a n s \frac{i}{j}<ans ji<ans时必须使用 < < <进行,即可取到先出现的情况而不取后出现的情况。最后输出 r i , r j r_i,r_j ri,rj

解法2:枚举 不使用浮点数

使用浮点数进行计算以及比较可能会由于其精度有限以至于无法表示真正的数学值。因此尽量使用整型量来完成计算。
算法和解法1是一样的。
枚举过程中判断条件为: i j ≥ A B ∧ i j < a n s \frac{i}{j} \ge \frac{A}{B}\land\frac{i}{j}<ans jiBAji<ans
其中ans为 r i r j \frac{ri}{rj} rjri
判断 i j ≥ A B \frac{i}{j} \ge \frac{A}{B} jiBA,可以使用十字相乘法,判断 i ⋅ B ≥ j ⋅ A i\cdot B\ge j\cdot A iBjA,这样可以避免使用浮点数。
同理,判断 i j < r i r j \frac{i}{j} < \frac{ri}{rj} ji<rjri,也可以写为 i ⋅ r j < j ⋅ r i i\cdot rj<j\cdot ri irj<jri
可以设 r i , r j ri,rj ri,rj的初值为0,表示还没有得到结果。
如果此时 i j ≥ A B \frac{i}{j} \ge \frac{A}{B} jiBA而且 r i , r j ri,rj ri,rj都为0,那么就使用 r i , r j ri,rj ri,rj记录当前的 i , j i,j i,j。或者当满足 i j ≥ A B ∧ i j < r i r j \frac{i}{j} \ge \frac{A}{B}\land\frac{i}{j}<\frac{ri}{rj} jiBAji<rjri时记录当前的 i , j i,j i,j
最终输出结果 r i , r j ri,rj ri,rj

【题解代码】

解法1:枚举 保存浮点数 A B \frac{A}{B} BA
  • 写法1:判断分子分母是否互质
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
int gcd(int a, int b)
{
	if(b == 0)
		return a;
	return gcd(b, a%b);
}
int main()
{
    int a, b, l, ri, rj;//ri,rj记录结果 
    double d, d1, ans = INF;//mind:最小差值 
    cin >> a >> b >> l;
    d = (double)a/b;//A/B
    for(int i = 1; i <= l; ++i)//i:A' j:B'
        for(int j = 1; j <= l; ++j) if(gcd(i, j) == 1)//最大公约数为1,二者互质
        {
			double d1 = (double)i/j;
            if(d1 >= d && d1-d < ans)
            {
                ans = d1-d;
                ri = i;
                rj = j;
            }
        }
    cout << ri << ' ' << rj;
    return 0;
}
  • 写法2:不判断互质
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
int main()
{
    int a, b, l, ri, rj;//ri,rj记录结果 
    double d, d1, ans = INF;//mind:最小差值 
    cin >> a >> b >> l;
    d = (double)a/b;//A/B
    for(int i = 1; i <= l; ++i)//i:A' j:B'
        for(int j = 1; j <= l; ++j)
        {
			double d1 = (double)i/j;
            if(d1 >= d && d1-d < ans)
            {
                ans = d1-d;
                ri = i;
                rj = j;
            }
        }
    cout << ri << ' ' << rj;
    return 0;
}
解法2:枚举 不使用浮点数
#include <bits/stdc++.h>
using namespace std;
int main()
{
	int a, b, l, ri = 0, rj = 0;
	cin >> a >> b >> l;
	for(int i = 1; i <= l; ++i)
		for(int j = 1; j <= l; ++j)
			if(i*b >= a*j && (ri == 0 && rj == 0 || i*rj < j*ri))
				ri = i, rj = j;
	cout << ri << ' ' << rj; 
	return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值