首页 > 其他分享 >Luogu P1298 最接近的分数 做题记录

Luogu P1298 最接近的分数 做题记录

时间:2023-04-30 21:11:25浏览次数:73  
标签:分数 P1298 int Luogu double dfrac error 节点

算是水紫,不过也学到一些有用的东西。


题意

给定正小数 \(N\)。求分子不大于 \(n\),分母不大于 \(m\) 的分数 \(\dfrac{n}{m}\),使得 \(\dfrac{n}{m}\) 的值与 \(N\) 最接近(这里的最接近指的是 \(|\dfrac{n}{m} - N|\) 最小)。

分析

首先,大部分人都可以想到一个暴力:枚举 \(i \in [1, m]\) 作为分子,计算出最佳分母 \(r_1 = \lfloor i \times N \rfloor, r_2 = \lceil i \times N\rceil\)。把 \(r_1, r_2\) 分别带进去看看那个更优就完事了。出去判边界之类的问题,复杂度 \(O(m)\)。由于 \(m \leq 10 ^ 7\),直接就艹过去了。如果这个都不会写可以看这里:暴力算法

然而我们肯定不会满足于这样的暴力算法。来点优雅的算法吧。


引入分数逼近。这里的分数逼近是指用用一个分数来逼近另一个分数,使得误差趋于零。例如,假设需要逼近的分数为 \(\dfrac{r}{s}\),有分数 \(\dfrac{u}{v} > \dfrac{r}{s}\)。那么有以下结论:

\[\dfrac{r}{s} \leq \dfrac{r + u}{s + v} \leq \dfrac{u}{v} \]

具体等号能不能取到记不清了,不过不影响。结论很好证明,下面证一下。


将 \(\dfrac{r + u}{s + v}\) 与 \(\dfrac{r}{s}\) 做减法,得到 \(\dfrac{r + u}{s + v} - \dfrac{r}{s} = \dfrac{(r + u)s - r(s + v)}{s(s + v)} = \dfrac{us- vr}{s(s + v)}\)。

因为 \(\dfrac{r}{s} < \dfrac{u}{v}\),两边同时乘以 \(sv\),得 \(vr < us\),即 \(us - vr > 0\)。

又因为 \(s(s + v) > 0\),所以 \(\dfrac{us - vr}{s(s + v)} > 0\)。证毕。

注意上面结论和证明成立的条件是 \(u, v, s, r > 0\)。


接下来引入 Stern-Brocot 树这个概念。

Stern-Brocot 树可以维护所有的正分数。这一点可以被我们用来解决这道题目。

首先介绍一下 Stern-Brocot 树。这个树由 \(\dfrac{0}{1}\) 和 \(\dfrac{1}{0}\) 两个分数开始。\(\dfrac{1}{0}\) 不大好定义,暂且把它当做 \(+ \infty\)。将这两个分数作为源节点

接下来,像我们刚才讨论的分数逼近,将 \(\dfrac{0}{1}\) 和 \(\dfrac{1}{0}\) 的分子分母分别相加,得到另外一个分数 \(\dfrac{1}{1}\)。这个分数确实在 \(\dfrac{0}{1}\) 与 \(\dfrac{1}{0}\) 之间。\(\dfrac{1}{1}\) 被成为第 \(1\) 层迭代后的节点。

同样的,将 \(\dfrac{1}{1}\) 与 \(\dfrac{0}{1}, \dfrac{1}{0}\) 分别进行操作,得到两个分数,称为第二次迭代。

所以我们得到了 Stern-Brocot 树的构建基础:将 \(\dfrac{a}{b}\) 与 \(\dfrac{c}{d}\) 分子分母分别相加,得到 \(\dfrac{a + c}{b + d}\) 作为下一轮迭代的节点。

例如,进行三次操作后,这棵树就会变成这样:

\[\begin{array}{c} \dfrac{0}{1}, \dfrac{1}{1}, \dfrac{1}{0} \\\\ \dfrac{0}{1}, \dfrac{1}{2}, \dfrac{1}{1}, \dfrac{2}{1}, \dfrac{1}{0} \\\\ \dfrac{0}{1}, \dfrac{1}{3}, \dfrac{1}{2}, \dfrac{2}{3}, \dfrac{1}{1}, \dfrac{3}{2}, \dfrac{2}{1}, \dfrac{3}{1}, \dfrac{1}{0} \end{array} \]

注意,某些节点(就是第 \(i\) 层存在,第 \(i + 1\) 层也存在的节点),实际上在第 \(i + 1\) 层是不会出现的。只是为了方便比较加了上去。

可以看到,第三层的第二个分数 \(\dfrac{1}{3}\) 就是左右两边两个数分子分母分别相加的和。第四个,第六个和第八个以此类推。

下面是来自 OI-wiki 的一张图。

Stern-Brocot树

刚才所提到的不存在的节点就是虚线相连的那些节点。可以看到,这棵树具有二叉结构。因此在这棵树上搜索只需要花费 \(O(\log_2 n)\) 的时间。非常优秀。这样对于这道题,我们就可以把小数 \(N\) 从第一层开始向下搜索。如果当前节点值大于 \(N\),那么向左递归。否则向右递归,直到分子或分母大于 \(n\) 或 \(m\)。时间复杂度肯定是 \(O(\log n)\)。(假设 \(n, m\) 同阶)。

关于最简性的证明可以看 OI-wiki 上的解释。这里不再赘述。


这道题的思路就讲解完了。注意别忘了判断多解的情况。由于刚才提到,Stern-Brocot 树具有最简性,因此放心的判断当前分数值与 \(N\) 的误差和原来的是否一样就可以了。

卡常顺便卡了个 rank1。欢迎来踩。

代码

#include <algorithm>
#include <cstdio>

using PII = std::pair<int, int>;
double N, m_error;
int n, m;
PII ans(0, 1);
bool flag = false;

double fabs(double x) {
    return x < 0 ? -x : x;
}
inline void get(double N, int a = 0, int b = 1, int c = 1, int d = 0) {
	int x = a + c, y = b + d;
	if (x > n || y > m) return;
	double error = (double)x / y - N;
	if (fabs(error) == m_error) flag = true;
	if (fabs(error) < m_error) {
		flag = false; ans = {x, y}; m_error = fabs(error);
		if (error == 0) return;
	}
	if (error < 0) get(N, x, y, c, d);
	else get(N, a, b, x, y);
}

int main() {
	scanf("%d%d", &n, &m);
	scanf("%lf", &N); m_error = N; get(N);
	if (flag) puts("TOO MANY");
	else printf("%d/%d", ans.first, ans.second);
	return 0;
}

明天就是五一劳动节。在这里提前祝大家五一快乐,多多点赞。

标签:分数,P1298,int,Luogu,double,dfrac,error,节点
From: https://www.cnblogs.com/LcyRegister/p/17365774.html

相关文章

  • 【专栏精选】实战:使用LeanCloud上传玩家分数,实现排行榜
    本文节选自洪流学堂公众号技术专栏《大话Unity2019》,未经允许不可转载。洪流学堂公众号回复专栏,查看更多专栏文章。洪流学堂,让你快人几步。你好,我是郑洪智。小新:“有了用户登录后,我们总要拿来做点什么事情吧?”大智:“有了用户登陆信息之后,就可以针对用户来存储他自己的信息了,比如......
  • 我的第一个项目(十二) :分数和生命值的更新(后端增删查改的"改")
    好家伙,写后端,这多是一件美逝.关于这个项目的代码前面的博客有写 我的第一个独立项目-随笔分类-养肥胖虎-博客园(cnblogs.com) 现在,我们登陆进去了,我开始和敌人战斗,诶,打到一百分了,我现在要把这个分数保存起来  1.前端先把测试样例写好 随便写一个测试样......
  • 0/1分数规划学习笔记
    #0/1分数规划学习笔记——bysunzz3183------------##介绍$0/1$分数规划是指,给定整数$a_1,a_2,\cdots,a_n,b_1,b_2,\cdots,b_n$,求一组解$x_i,x_i\in\left\{0,1\right\}$,使下面的式子最大化:$$\frac{\sum_{i=1}^{n}a_i\timesx_i}{\sum_{i=1}^{n}b_i\timesx_i......
  • SNMPV3监控华为设备只能监控到部分数据的解决方法
    最近在做Zabbix监控二次开发的一个项目,需要用到snmp监控被管设备的各种信息,比如风扇状态等PS:本项目之前配置的都是SNMPV3协议(即需要加密的snmp)经华为资料得到它们的MIB库(一个snmp协议的oid集合),发现并不能根据他给的oid获取数据于是用snmpWalk爬取所有数据,发现只能爬到一部分,并......
  • 修改国家开放大学分数
     transform是css3属性。有时候UI给的原型图是图片旋转出来的,而作为前端应该怎样取使用旋转还原原型图呢。transform:scale(0.5) 缩放0.5倍transform:rotate(90deg)transform:rotate(-270deg) 顺时针旋转90°transform:rotateX(90deg)沿x轴旋转90度transform:rotateY(180deg)沿y轴旋......
  • Luogu P8007
    Upd.2022.2.3代码写的太烂,删了(题目传送门这题如果不仔细分析的话,很容易被当成DP白白浪费很多时间(就像我)。首先根据题意,可以认为左右括号是一种相互“抵消”的关系:对于每个左括号,它右面总要有且仅有一个对应的右括号与其配对,才能使其成为一个合法括号序列。在已知序列不无限......
  • Luogu P8496
    题面场外菜鸡whker听说你谷添加国赛新题立刻前来围观首先我们看到本题对于众数的定义,很容易想到通过权值线段树求解。(类似这题,但本题不需要可持久化)对于一个序列,我们维护一个deque和一个动态开点权值线段树。deque表示序列本身,线段树每个节点记录值在\([l,r]\)中的元素......
  • Luogu P3336
    因为我也是看了大佬的题解才写的(第一问),自认为自己讲得不可能比他们再好了,但是因为好多第二问的题解都被hack了,所以这里详细讲一下第二问的正确做法。初中平几课堂开课啦其实思路很简单,利用贪心的思想,能往上走就往上走,能走多高就走多高,来看这个图:点\(A\)是当前点,点\(B\)是......
  • Luogu P1999
    题目传送门初中数学老师在平面几何的第一节课就和我们说过:点动成线,线动成面,面动成体。即,由\(i-1\)维元素变化到\(i\)维的过程,就可以认为是将\(i-1\)维物体沿第\(i\)个方向平移的过程。因此我们考虑一个二维的正方形平移得到三维的正方体的过程:如果我们以平面的个......
  • mysql单表备份部分数据且数据量较大时
    --复制表结构CREATETABLEtableB(LIKEtableA);--插入筛选数据INSERTintotableBSELECT*fromtableAwhereXXX=?;--重命名,替换renametabletableAtotableC;renametabletableBtotableA;--删除旧表DROPTABLEtableC;......