【P1763 埃及分数】题解

题目链接

题目

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

\begin{aligned} \frac{19}{45} &= \frac{1}{3} + \frac{1}{12} + \frac{1}{180}\\ \frac{19}{45} &= \frac{1}{3} + \frac{1}{15} + \frac{1}{45}\\ \frac{19}{45} &= \frac{1}{3} + \frac{1}{18} + \frac{1}{30}\\ \frac{19}{45} &= \frac{1}{4} + \frac{1}{6} + \frac{1}{180}\\ \frac{19}{45} &= \frac{1}{5} + \frac{1}{6} + \frac{1}{18}\\ \end{aligned}

最好的是最后一种,因为 \(\dfrac{1}{18}\)\(\dfrac{1}{180}, \dfrac{1}{45}, \dfrac{1}{30}, \dfrac{1}{18}\) 都大。
注意,可能有多个最优解。如:

\begin{aligned} \frac{59}{211} &= \frac{1}{4} + \frac{1}{36} + \frac{1}{633} + \frac{1}{3798}\\ \frac{59}{211} &= \frac{1}{6} + \frac{1}{9} + \frac{1}{633} + \frac{1}{3798}\\ \end{aligned}

由于方法一与方法二中,最小的分数相同,因此二者均是最优解。

给出 \(a,b\),编程计算最好的表达方式。保证最优解满足:最小的分数 \(\ge \cfrac{1}{10^7}\)

思路

方法就是爆搜。

可以用迭代加深搜索来实现,枚举总共用多少个分数,然后搜下去。

注意输入的数要先约分,不然我像我一样一直RE一个点。

总结

  1. 遇到此类题目,不要害怕,要勇敢去打。
  2. 对于题目中不确定的(例如个数),可以用迭代加深搜索,因为前面的全部搜一次都不够最后一层搜得大。
  3. 题目中涉及分数运算要经常记得通分。

Code


// Problem: P1763 埃及分数
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1763
// Memory Limit: 128 MB
// Time Limit: 1000 ms
// Powered by CP Editor (https://github.com/cpeditor/cpeditor)

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
//#define M
//#define mo
#define N 10000010
int n, m, i, j, k; 
int ans[N], tot[N], flg; 

int gcd(int x, int y)
{
	while(y)
	{
		int z=x%y; 
		x=y; 
		y=z; 
	}
	return x; 
}

void dfs(int lmt, int k, int x, int y)
{
//	if(k==3) return ; 
//	if(k<2)
//	 printf(">%lld %lld %lld %lld\n", lmt, k, x, y); 
	if(k>lmt)
	{
		// printf("%lld %lld\n", x, y); 
		if(x==n&&y==m)
		{
			if(ans[lmt]<tot[lmt])
				for(int i=1; i<=lmt; ++i) tot[i]=ans[i]; 
			flg=1; 
		}
		return ; 
	}
	int xn=n*y-m*x, yn=m*y; 
	if(yn==0) return ; 
	int i, z, newx, newy;  
	// printf("(%lld, %lld<=i*%lld)\n", ans[k-1]+1, m*y, (n*y-m*x)); 
//	if(k<3)
//	 printf("%lld<=i<=%lld\n", max(ans[k-1]+1, yn/xn), yn*(lmt-k+1)/xn); 
	for(i=max(ans[k-1]+1, yn/xn); i<=yn*(lmt-k+1)/xn; ++i)
	{
		
		ans[k]=i; 
		newx=x*i+y; newy=y*i; 
		if(m*newx>n*newy) continue; 
		z=gcd(newx, newy); 
		if(z>1) newx/=z, newy/=z; 
//		 printf("(%lld) ", z); 
		dfs(lmt, k+1, newx, newy); 
	}
}

signed main()
{
//	freopen("tiaoshi.in", "r", stdin); 
//	freopen("tiaoshi.out", "w", stdout); 
	n=read(); m=read(); 
	k=gcd(n, m); n/=k; m/=k; 
	for(i=ans[0]=1; !flg; ++i)
	{
//		printf("%lld\n", i); 
		tot[i]=1e12, dfs(i, 1, 0, 1); 
	}
		
	// printf("%lld\n", i); 
	for(j=1; j<i; ++j) printf("%lld ", tot[j]); 
	return 0; 
}
posted @ 2021-12-23 18:24  zhangtingxi  阅读(390)  评论(0编辑  收藏  举报