題解 埃及分數
問題 C: 埃及分數
題目描述
在古埃及,人們使用單位分數的和 (形如 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。
輸出
若干個數,自小到大排列,依次是單位分數的分母。
樣例輸入
19 45
樣例輸出
5 6 18
題解
這道題目就是個暴力
首先先抨擊朱老師沒有打spj,而此題需要spj
例如:
$\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}$
而這兩個答案都可以
我們從1開始列舉分母的個數,然後我們假設當前分母為$\frac{a}{b}$
前面分母的最大值為$t$ $deepthset$表示分母的個數
$(max(t,\frac{b}{a})+1->\frac{b}{a}*(deepest-depth+1))-1$
這樣,我們驚奇地發現,當$n$,$m$在1000以內時,跑得非常快
於是這道題目就做完了,時間複雜度……
程式碼
\#include <cstdio> #include <algorithm> #include <cstring> using namespace std; typedef long long LL; const int N=1020; LL ans[N],sum[N]; int n,m,deepest; int gcd(int __m, int __n){ while (__n != 0){ int __t = __m % __n; __m = __n; __n = __t; } return __m; } LL gcd(LL __m, LL __n){ while (__n != 0){ LL __t = __m % __n; __m = __n; __n = __t; } return __m; } bool dfs(LL a,LL b,LL depth,LL t){ if (depth==deepest){ if ((b%a)!=0) return 0; ans[depth]=b/a; bool Flag=false;//判斷答案是否更優 for (int i=depth;i>=0;--i){ if (ans[i]!=sum[i]) { if (ans[i]<sum[i]) Flag=true; else Flag=false; break; } } if (Flag) { for (int i=0;i<=depth;++i) sum[i]=ans[i]; } return 1; } bool flag=false; for (int i=max(t,b/a)+1;i<(b/a*(deepest-depth+1));++i){ ans[depth]=i; if (dfs(a*i-b,b*i,depth+1,i)) flag=true; } return flag; } int main(){ memset(sum,0x3f3f3f,sizeof(sum)); scanf("%d%d",&n,&m); int Gcd=gcd(n,m); n/=Gcd,m/=Gcd; if (n==1){ printf("%d\n",m); return 0; } for (int i=1;;++i){ deepest=i; if (dfs(n,m,0,m/n)){ for (int j=0;j<=i;++j) printf("%d%c",sum[j],j==i?'n':' '); break; } } return 0; }