题目描述 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(0
输入描述 Input Description
a b
输出描述 Output Description
若干个数,自小到大排列,依次是单位分数的分母。
样例输入 Sample Input
19 45
样例输出 Sample Output
5 6 18
题解:
好的我知道我已经废掉了。
枚举加数,判断是否有解,由于加数个数的上限不好确定,所以是迭代加深搜索。
搜索的时候定义状态为当前深度,允许深度,前一个分母(由于是递增的顺序枚举分母),还需要凑成的分数值(写成分子分母的形式)。
用实数的话。。。很麻烦,掉精度什么的
如果只剩下一个分数的位置的话,就直接判断是否是单位分数,然后判断是否可以更新就可以了。
重点在枚举分母上,因为分母的范围其实很大。由于定义了是从小到大枚举分母,所以当前分母的最小值应该比前一个大(这也是为什么要记录下前一个分母的值),这就确定了分母的下界。至于上界,然后由于分母单增,所以分数值单减。设我们还剩下x个加数需要填,则每个分数均摊下来都应该是a/(bx),而当前分数是最大的,所以它一定不能小于这个值,即分母不能大于(bx/a)。在规定的范围内枚举更新就可以了。
woc我有罪我之前的代码有毛病,现已更正。。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#define ll long long
using namespace std;
const int N = 1000 + 10;
inline ll gcd(ll a,ll b) {return b==0?a:gcd(b,a%b);}
void modify(ll &a,ll &b){
ll k=gcd(a,b);
a/=k,b/=k;
}
ll a,b;
ll c[N],tmp[N];
bool check(int n){
if(c[n]==-1) return true;
if(c[n]>tmp[n]) return true;
return false;
}
bool dfs(int d,int pre,int limit,ll a,ll b){
if(d>limit) return false;
modify(a,b);
if(d==limit){
if(a==1&&b>pre){
tmp[limit]=b;
if(check(limit)){
for(int i=1;i<=limit;++i) c[i]=tmp[i];
return true;
}
}
return false;
}
bool tc=0;
for(int i=pre+1;i<=ceil(1.0*b*(limit-d+1)/a);++i){
tmp[d]=i;
tc|=dfs(d+1,i,limit,i*a-b,i*b);
tmp[d]=0;
}
return tc;
}
int main(){
cin>>a>>b;
int ans=1;
modify(a,b);
memset(c,-1,sizeof(c));
for(;;++ans){
if(dfs(1,1,ans,a,b)) {
for(int i=1;i<=ans;++i) printf("%d ",c[i]);
break;
}
}
return 0;
}