您的位置:首页 > 其它

限界深搜——埃及分数 两种剪枝

2016-05-20 20:23 453 查看
Description

问题描述:在古埃及,人们使用单位分数的和(形如1/a的, a是自然数)表示一切有理数。 如:2/3=1/2+1/6,但不允许2/3=1/3+1/3,因为加数中有相同的。 对于一个分数a/b,表示方法有很多种,但是哪种最好呢? 首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越 好。 如:

19/45 = 1/3 + 1/12 + 1/180

19/45 = 1/3 + 1/15 + 1/45

19/45 = 1/3 + 1/18 + 1/30

19/45 = 1/4 + 1/6 + 1/180

19/45 = 1/5 + 1/6 + 1/18.

最好的是最后一种,因为1/18 比 1/180,1/45,1/30,1/180都大。 给出a,b, 编程计算最好的表达方式。

Input

输入:a b

Output

输出:若干个数,自小到大排列,依次是单位分数的分母。

Sample Input

19 45

Sample Output

5 6 18

Hint

【数据规模】

0<a<b<1000

题目本身是一个简单的限界深搜,每次枚举分母,每次x/y-1/i,直到当前深度=maxdepth的时候,判断最后剩下的分数,也即最后一个答案是否符合埃及分数的约定。

有两个至关重要的剪枝:

1.保证枚举的单调性——每次从小于x/y的最大分数从大到小开始枚举,以节省枚举时空。

2.基于剪枝1,如果“即使以后的每次都加上当前枚举分数(保证了最大)都<=待减分数”,则这个枚举是无效的,可以忽略。

//涉及gcd,用了long long
#include<iostream>
#include<cstdio>
#include<cstring>
#include<iomanip>
#include<cmath>
using namespace std;
typedef long long ll;
ll A,B,mn,maxdepth,temp[100005]={0},ans[100005]={0};
ll GCD(ll a,ll b)
{
if(!b)return a;
return GCD(b,a%b);
}
ll getst(ll x,ll y)//找到比 x/y 小的 最大的 1/i
{
for(ll i=2;;i++)if(x*i>y)return i;
}
bool judge(ll lim)
{
if(ans[0]==0)return 1;//答案数组为空
return(temp[lim]<ans[lim]);//因为保证了递减枚举,则temp[lim]肯定为最小的一个,现在要求最小分数最大,即分母最小。
return 0;
}
bool DFS(ll depth,ll minn,ll x,ll y)
{
bool flag=0;
if(depth==maxdepth)
{
if(y%x)return 0;//不符合埃及分数的定义,如2/3,4/5;而1/2,2/4,4/8等都是符合要求的。
temp[depth]=y/x;//注意约分
if(judge(depth))
memcpy(ans,temp,sizeof(temp));//存在更优解,更新答案
return 1;
}
minn=max(minn,getst(x,y));//取得新下界,注意这里取的是max(分母),以防止漏除枚举。
for(ll i=minn;;i++)
{
if((maxdepth-depth+1)*y<=x*i)break;//剪枝2,这里把(maxdepth-depth+1)*(1/i)<=x/y 做了变形。
ll newy=y*i;
ll newx=x*i-y;//做差
temp[depth]=i;//枚举解放入temp数组
ll gcd=GCD(newx,newy);//取得新分数分子分母最小公因数,以便约分。
if(DFS(depth+1,minn+1,newx/gcd,newy/gcd))flag=1;//minn一定要+1,不然会出现重复枚举
//如果下一层搜索结果可行,则说明当前枚举可行
}
return flag;
}
void work(void)
{
scanf("%lld%lld",&A,&B);
for(maxdepth=1;;maxdepth++)
{
mn=getst(A,B);
memset(temp,0,sizeof(temp));
memset(ans,0,sizeof(ans));//不要忘了清空ans数组,每一个层次都可能存在最优解
if(DFS(0,mn,A,B))break;//要求加数最少,找到一个可行解就退出
}
for(ll i=0;i<=maxdepth;i++)printf("%lld ",ans[i]);//不要忘了从0开始输出。
}
int main(){
work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: