【紫书】UVA12558 埃及分数 Egyptian Fractions (HARD version)

题目提交点

UVA12558

解题思路

这个题目数据范围有点小, 我刚开始想的是用dfs, 可是遇到了一些问题, 不知道他什么时候结束,最优解有几项,新加入的埃及分数取什么值最好等等问题困惑着我。
顺着理一下思路,需要暴力做这题,但是不知道上限,非常符合迭代加深搜的算法规则,OK,开始分析细节。

①迭代加深搜,搜到了就结束,没搜到就继续搜, dfs层数代表着埃及分数的项数。

②dfs剪枝, 层数超过了,或者累加的答案已经超过所要的答案了,进行剪枝操作。

③对埃及分数取值问题:
a, b 分别为已经累加到的数分子与分母,A, B分别为所需要的分子与分母。
设1/c 为所加的埃及分数, 便有
1/c + a / b <= A / B ==> 1/c <= A/B - a/b = (Ab - aB)/Bb ==> c >= Bb/(Ab - aB)
再与上一个已经取的值做比较,取最大,那么这个数就是最小的c值。

不一定这个最小的值是最好的,所以得不断的尝试, 让这个值自加。
设no 为最小的c的取值,max_d 为此dfs最大能到的层数, d 为当前层数。
当这个新取的埃及分数小到无论怎么取都达不到的值时,结束此埃及分数的取值,即 a/b + (max_d - d) / no < A / B

代码

#include<bits/stdc++.h>
#include<unordered_set>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)

typedef long long LL;
int A, B;
unordered_set<LL> R;

inline int readint()
{
	int x;
	scanf("%d", &x);
	return x;
}

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

LL get_first(LL a, LL b, LL last)
{
	return max((B * b) / (A * b - a * B), last + 1);
}

bool judge(const vector<LL>& D, const vector<LL>& ans)
{	
	if (ans.empty()) return true;
	for (int i = ans.size() - 1; ~i; --i) if (D[i] != ans[i])
		return D[i] < ans[i];
	return false;
}

void dfs(LL a, LL b, const int d, const int max_d, vector<LL>& D, vector<LL>& ans)
{
	if (d > max_d) return;
	if (a * B > A * b) return;
	if (a == A && b == B)
	{
		if (judge(D, ans)) ans = D;
		return;
	}
		
	LL no = get_first(a, b, D.empty() ? 1LL : D.back());
	while (true)
	{
		if (a * B * no + (max_d - d) * B * b < A * b * no) break;
		if (!R.count(no))
		{
			LL na = a * no + b, nb = b * no, g = gcd(na, nb);
			D.push_back(no);
			dfs(na / g, nb / g, d + 1, max_d, D, ans);
			D.pop_back();	
		}
		no++;
	}
}

int main()
{
#ifdef LOCAL
	freopen("data.in", "r", stdin);
#endif
	
	int T = readint();
	_rep(kase, 1, T)
	{
		A = readint(), B = readint();
		int k = readint();
		R.clear();
		_for(i, 0, k) R.insert(readint() * 1LL);
		
		for (int d = 2; ; ++d) {
			vector<LL> D, ans;
			dfs(0, 1, 0, d, D, ans);
			
			if (!ans.empty()) {
				printf("Case %d: %d/%d=", kase, A, B);
				int sz = ans.size();
				_for(i, 0, sz)printf("1/%lld%s", ans[i], (i == sz - 1) ? "\n" : "+");
				break;
			}
		}
	}
	return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
埃及分数是一种特殊的分数表示方式,它表示为若干个分数之和,其中每个分数的分子为1,分母为正整数,且分母互不相同。例如,6/7可以表示为1/2 + 1/3 + 1/42。 贪心算法可以用来求解埃及分数,具体实现如下: 1. 输入一个正分数x,初始化一个空数组fractions用于存放埃及分数。 2. 从最大的分母开始,不断尝试将x表示为1/denominator的形式,直到x变为0或者分母为1为止。 3. 对于每个分母,计算出当前能够表示的最大的1/denominator的分数,将该分数加入fractions数组中,并将x减去该分数。 4. 如果x已经为0,返回fractions数组作为结果;否则,重新从最大的分母开始尝试表示x,重复步骤3和4,直到x变为0为止。 以下是具体的C语言实现: ```c #include <stdio.h> int main() { double x; scanf("%lf", &x); // 输入待转换的分数 int denominator = 1; // 分母从1开始 int fractions[100], count = 0; // 存放埃及分数的数组和计数器 while (x > 0) // 当x不为0时继续循环 { int numerator = (int) (denominator * x + 1); // 计算当前能够表示的最大分数的分子 fractions[count++] = numerator / denominator; // 将该分数加入fractions数组 x -= 1.0 * fractions[count-1] / denominator; // 减去该分数 denominator = numerator % denominator; // 更新分母 } printf("Egyptian fractions: "); for (int i = 0; i < count; i++) printf("1/%d + ", fractions[i]); printf("\b\b \n"); // 输出埃及分数 return 0; } ``` 运行该程序,输入一个待转换的分数,即可得到其对应的埃及分数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值