#10022. 「一本通 1.3 练习 1」埃及分数

版权声明:老江的博客,转载请告知老江 https://blog.csdn.net/qq_42367531/article/details/86069571

因为题目实在是太复杂麻烦了,所以我就直接截屏了啊,望谅解) 

先理解一下题目吧(我知道大家都看懂了,那我就不多说了,按老规矩给你们稍微总结一下吧)

  • 首先,这道题我们要求的是最大当中的最小,就是说最后一个的分数的分母是最大的,这个就是最大的,然后我们又要让最大的这个分母最小。这个很关键因为这个关乎到了我们 dfs 当中的判断换个角度想想如果没有这个的话,这道题就很简单了,直接搜索找到加起来的值是相同的就退出。
  • 第二点就是因为这是分数,也就是说它有两部分,这就意味着如果我们直接就用这个分数的值来搜索的话,肯定会出问题:
  1. 你在定义的时候,如果是除不尽的小数怎么办?
  2. 我们的分子分母怎么样枚举,一起加还是说一起减?分开你要怎么折腾?
  3. dfs中的那个当前搜索的层数,分子分母各定义一个还是说分开定义

所以这些都是我们要考虑的,这些看起来很白痴的问题就为我们的搜索起到了一个极为重要的铺垫作用、

  • 最简分数要用gcd的吧(求最大公约数的函数)
  • 用来输出的数组要有吧
  • 如果把dfs看成一棵树的话,这个最大层数和最小层数要有吧
  • 还有就是说我们在搜索判断的时候,更新这个保存的数组要有吧
  • 我们搜素判断的时候,一个bool要有吧,还有就是说dfs我们直接用bool,这样我们就直接搜索成功返回再输入,如果在dfs当中进行的话,可能会出错
  • 最后的就是剪枝了吧

接下来我就重点来讲一下我们的一个小小的东西(叫剪枝)

很好这是我们其中的一个要点。就是说我们的分子一定要大于等于\frac{b}{a},这个是必然的

 这是又一个点,也是我们在搜索的时候必须知道的,所以说如果没有这一个的话你再怎么搜索都是会错的,要不超时要不就是说整个的运行的错误

附上大佬的博客

整体的一些就是大概这样的啦。剩下的我也不多讲,看代码吧 

【代码实现1:我的理解性代码300多ms】

/*我不得不再打一遍代码
对于这个具体的原理我还是要好好研究一下
大概有那么一点点思路
就是说我们在递归的时候一直在更改我们的分子和分母
然后我们还要判断这么分子和分母到底可不可以
而且还有判断他们相加到底等不等于这个原数
如果等于的话,我们就把当前这个成功的答案记录到c数组当中
记录完之后继续寻找,继续找更小的
这个就是这道题的主要思路*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll a,b,c[210000],shi[210000];/*a,b表示原数,c是用来记录结果的,
shi数组是用来记录我们在搜索当中找到的分母,他们的分子默认为1*/ 
int maxn,tt=99999;//maxn是我们目前确定的最大层数,tt是配套c数组的层数 
ll zxfm(ll p,ll q)//我们搜索到的最小层数 
{
	for(int i=2;;i++)
	{
		if(q<=p*i)return i;
	}
}
ll gcd(ll a,ll b)//最大公约数 
{
	if(a==0)return b;
	return gcd(b%a,a);
}
bool pd(int tp)//pd剪枝吧,就是不能比之前记录过的最小值要大 
{
	if(maxn>tt)return false;
	if(shi[maxn]>c[tt])return false;
	return true; 
}
bool dfs(int d,ll minx,ll x,ll y)/*d是我们当前搜索的层数,minx是最小层数,
x,y是第一次是原数 
x,y确切来讲应该是我们在不断的搜索当中确定的一个个分子和分母\
然而这个分子和分母一定要是倍数关系,也就是说分子可以变为1*/
{
	if(d==maxn)//如果这个层数已经到了最大层数 
	{
		if(y%x!=0)return false;//倍数关系不是最简,返回重新计算 
		shi[d]=y/x;//否则记录下来他们的商 
		if(pd(d))//pd一下能不能剪枝 
		{
			for(int i=1;i<=maxn;i++)c[i]=shi[i];//成功就更新一下 
			tt=maxn;//把最大层数也更新一下 
		}
		return true;//返回上一层 
	}
	minx=max(minx,zxfm(x,y));/*我觉得主要原因是因为就是说,minx记录的是最小层数,
	然后zxfm找到的也是最小层数,这个主要是为了后面ab更改之后在更换,因为层数越大,才表示
	我们真正的最极限的情况*/ 
	bool bkk=false;//bkk用在判断当中 
	for(int i=minx;i;i++)
	{
		if((maxn-d+1)*y<=i*x)break;/*
		如果最大个数*最大层数(或者最大层数的上一层)[层数越往上值越大] 
		小于最小个数*最小层数的话,这个情况本身就是有问题的
		(简单来讲,这应该算是一个剪枝条件)*/ 
		shi[d]=i;//
		ll xx=x*i-y,yy=y*i,GCD=gcd(xx,yy);/*最小极限和最大极限,
		无论如何a*i-b是不等于0的,
		这个可以说是当前最小的分数的极限(分子很小,分母很大)。
		找到之后看看有没有公约数*/ 
		if(dfs(d+1,i+1,xx/GCD,yy/GCD))bkk=true;//继续递归下一层的 
	}
	return bkk;
}
int main()
{
	scanf("%lld%lld",&a,&b);//输入 
	if(a%b==0)/*如果是倍数关系,比如说5/10的话,那么公约数是5,他们的答案就是1/2,1和2*/ 
	{
		ll GCD=gcd(a,b);
		printf("%lld %lld",a/GCD,b/GCD);
		return 0;
	}
 	ll minn=zxfm(a,b);//找到最小层数 
	maxn=2;//maxn是最大层数,但是我暂时还不到为什么初始化是2 
	do{//do...while的意思就是说先做了再判断 
		memset(c,63,sizeof(c));//初始化 
		if(dfs(1,minn,a,b))//递归 
		{
			for(int i=1;i<=maxn;i++)printf("%d ",c[i]);
			printf("\n");//找到答案就输出 
			return 0;
		}
		maxn++;//找不到就增加一个继续找 
	}while(maxn);
	return 0;
}
/*总结就一句话:我他妈一直加啊加,然后除啊除,然后就找到答案了*/

【代码实现2:大佬的代码300多ms】

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
ll a[1010],s[1010],maxd;
inline ll max(ll x,ll y){return x>y?x:y;}//最大值 
inline ll get(ll aa,ll bb){return bb/aa+1;}//函数函数 
ll gcd(ll aa,ll bb){return (!bb)?aa:gcd(bb,aa%bb);}//最大公约数 
bool bijiao(ll x)
{
    for(ll i=x;i>=0;i--) 
        if(a[i]!=s[i])return s[i]<0||a[i]<s[i];
    return false;
}
bool dfs(ll k,ll fa,ll aa,ll bb)
{
    if(k==maxd)
    {
        if(bb%aa)return false;
        a[k]=bb/aa;
        if(bijiao(k))
        { 
            for(ll i=0;i<=k+1;i++)
			{
				s[i]=a[i];
			}
		}
        return true;
    }
    else
    {
        bool bk=false;
        fa=max(fa,get(aa,bb));
        for(ll i=fa;;i++)
        {
            if((maxd+1-k)*bb<=aa*i)break;
            a[k]=i;
            ll ax=aa*i-bb,bx=bb*i;
            ll gg=gcd(ax,bx);
            if(dfs(k+1,i+1,ax/gg,bx/gg))bk=true;
        }
        return bk;
    }
}
int main()
{
    ll a,b;scanf("%lld%lld",&a,&b);
    bool bk=false;
    for(maxd=1;maxd<=100;maxd++)
    {
        memset(s,-1,sizeof(s));
        if(dfs(0,get(a,b),a,b)){bk=true;break;}
    }
    if(bk)
    {
        for(ll i=0;i<maxd;i++)printf("%lld ",s[i]);
        printf("%lld\n",s[maxd]);
    }
    return 0;
}

 大致就结束了吧,谢谢。

扫描二维码关注公众号,回复: 4844693 查看本文章

 

猜你喜欢

转载自blog.csdn.net/qq_42367531/article/details/86069571