星星之火OIer:迭代加深——埃及分数

迭代加深前置知识请看这里

题目描述

题目分析

这道题有两个限定条件,一个是要求分数的个数最小,还有一个是最小的分数最大

直接dfs是肯定不行的,你只能满足其中一个条件

于是我们就想到了迭代加深

其中迭代加深限定的深度是用多少个分数

然后一波判断+比较就出来了

补充知识

斐波那契提出一种用贪心求解埃及分数的方法

  • 先使a,b互质,即将原分数化成最简,且a<b
  • b/a=q\dots r,可得r=b-a*q
  • \frac{a}{b}记作\frac{a}{b}=\frac{1}{q+1}+\frac{a-r}{b*(q+1)}
  • \because r=b-a*q……①
  • \frac{b+a-r}{b*(q+1)}……②
  • 将①带入②就可以得到\frac{a}{b}
  • 而②通分后就是\frac{1}{q+1}+\frac{a-r}{b*(q+1)}
  • 然后我们就可以得到其中一个分数是\frac{1}{q+1}
  • 再把剩下的\frac{a-r}{b*(q+1)}继续分解,直到得到答案

于是我们就可以上代码了

代码实现::

#include<cstdio>
#include<cstring>
#define ll long long
#define rt
inline void read(long long &x) {
	x=0;
	int f=1;
	char s=getchar();
	for(;s<48||s>57;s=getchar())
		if(s=='-')
			f=-1;
	for(;s>47&&s<58;s=getchar())
		x=x*10+s-48;
	x*=f;
}
inline void pr(long long x) {
	if(x<0)
		putchar('-'),x=-x;
	if(x>9)
		pr(x/10);
	putchar(x%10+48);
}//快读快输不解释
ll ans[15],d[15],a,b,depth;
bool flag;
ll gcd(ll a,ll b) {//辗转相除法
	ll t=a%b;
	while(t)
		a=b,b=t,t=a%b;
	return b;
}
void iddfs(ll a,ll b,int k) {
	if(k>depth)//超越最大深度了,而且还没有答案,必须return
		rt;
	if(b%a==0&&(b/a)>d[k-1]) {//d是答案数组
		d[k]=b/a;//答案
		if(!flag||d[k]<ans[k])//没找到||最大的小于最小的
			memcpy(ans,d,sizeof(d));
		flag=1;//已经找到答案
		rt;
	}
	ll s=b/a;
	if(s<=d[k-1])//上下限
		s=d[k-1]+1;
	ll t=b*(depth-k+1)/a;
	if(flag&&t>=ans[depth])
		t=ans[depth]-1;
	for(ll i=s;i<=t;i++) {
		ll m=gcd(i*a-b,b*i);
		d[k]=i;
		iddfs((i*a-b)/m,b*i/m,k+1);//斐波那契贪心法求解
	}
}
int main() {
	d[0]=1;
	read(a),read(b);
	for(depth=1;depth<=10;depth++) {//至于为什么这里是10,能过就行了,只是开得更大就更保险,也更费时
		iddfs(a,b,1);//迭代加深
		if(flag) {//已经有答案
			pr(ans[1]);
			for(int i=2;i<=depth;i++)
				putchar(' '),pr(ans[i]);
			break;
		}
	}
}

大家还有什么不懂的,一起讨论讨论

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值