IDDFS--UESTC - 577 分数拆分

Description

任何一个分数都能才成若干个单位分数(形如1/a的, a是自然数)的和。

对于一个分数 ,表示方法有很多种,但是哪种最好呢?

首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越好,如果还是相同,那么第二小的分数越大越好,依次类推下去。

例如对于 ,下列方法都是合法的:

但是最好的是最后一种,因为 都大。

现在给出 ( ),求最好的表达方式。

Input

第一行有一个整数 ,表示有 ( )组测试数据,每组测试数据为一行包含 ( )。

Output

每组测试数据若干个数,自小到大排列,依次是单位分数的分母。

Sample Input


19 45

Sample Output

5 6 18



分析:本题采用迭代加深搜索算法,从小到大枚举深度上限maxd,每次执行只考虑深度不超过maxd的结点。深度上限还可以用来剪枝,按照分母递增的顺序来进行扩展,如果扩展到i层时,前i个分数之和为c/d,而第i个分数为1/e,则接下来至少还需要(a/b-c/d)/(1/e)个分数,总和才能达到a/b。


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;
const int maxn = 100 + 5;
LL v[maxn], ans[maxn];
int a, b, maxd;

LL gcd(LL a, LL b)
{
	return b == 0 ? a : gcd(b, a%b);
}

//返回满足 1/c <= a/b 的最小 c
inline int get_first(LL a, LL b)
{
	return b/a + 1;
}

//如果当前解v比目前最优解ans更优,更新ans
bool better(int d)
{
	for(int i=d; i>=0; i--) if(v[i] != ans[i]) {
		return ans[i] == -1 || v[i] < ans[i];
	}
	return false;
}

//当前深度为d,分母不能小于from,分数之和恰好为 aa/bb
bool dfs(int d, int from, LL aa, LL bb)
{
	if(d == maxd) {
		if(bb % aa) return false; //最后一层的解必须为埃及分数
		v[d] = bb/aa;
		if(better(d)) memcpy(ans, v, sizeof(LL)*(d+1));
		return true;
	}
	bool ok = false;
    from = max(from, get_first(aa, bb)); //枚举的起点
    for(int i = from ; ; i++) {
		//剪枝:如果剩下的 maxd+1-d 个分数全是 1/i,加起来仍不超过 aa/bb,无解
		if(bb * (maxd+1-d) <= i * aa) break;
		v[d] = i;
		//计算 aa/bb - 1/i 的值,设结果为 a2/b2
		LL a2 = aa * i - bb;
		LL b2 = bb * i;
		LL g = gcd(a2, b2);
		if(dfs(d+1, i+1, a2/g, b2/g)) ok = true;
    }
    return ok;
}

int main()
{
	int T;
	scanf("%d", &T);
	while(T--) {
		scanf("%d%d", &a, &b);
		int ok = 0;
		for(maxd=1; maxd<=100; maxd++) {
			memset(ans, -1, sizeof(ans));
			if(dfs(0, get_first(a, b), a, b)) { ok = 1; break; }
		}
		if(ok) {
			for(int i=0; i<=maxd; i++) printf("%d ", ans[i]);
			//printf("%d\n", ans[maxd]);
			printf("\n");
		} else printf("No solution.\n");
	}
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值