题目大意见刘汝佳先生的算法竞赛入门经典第二版--p206
此题第一个看上去就是想使用回朔算法,但是写程序时发现,根本没有办法知道此题回朔的深度是多少,也就是说解答树是无穷大的。
(原因是,无法知道深度,可能出现,分母很大,这样的分数的个数也就很大,导致解答树很大)。
显然,深度是此题的主要矛盾。如果无法确定深度是多少,那么此题写起来就会很麻烦。
这里我们以深度为方向来写这个程序,也就是迭代加深搜索,(也就是在解答树无线大的时候,同时也是要求深度最小的时候使用这种思想)。
下面是源程序:
//
// main.cpp
// 埃及分数
//
// Created by XD on 15/7/28.
// Copyright (c) 2015年 XD. All rights reserved.
//
#include <iostream>
#include <string>
#include <queue>
#include <stack>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include<vector>
#include <string.h>
#include <string>
#include <algorithm>
#include <set>
#include <map>
#include <cstdio>
#define ll long long
using namespace std ;
ll v[110] ;
ll ans[110] ;
int maxn ;
//欧几里得算法
ll gcd(ll a , ll b)
{
return b == 0 ?a : gcd(b,a%b) ;
}
ll max(int a , ll b)
{
return a >b ? a : b ;
}
//获得第一个分数的分母 即找到c使得 1/c <= a / b
ll get_first(ll a , ll b )
{
ll c ;
c = b / a ;
if (b%a) {
c+= 1 ;
}
return c ;
}
//判断当前的答案是否比以前的答案更好 ans[i] ==-1 是初始状态下
bool better(ll d)
{
for (ll i = d ; i>= 0; i--) {
if (ans[i] != v[i]) {
return ans[i]==-1 || ans[i] > v[i] ;
}
}
return false ;
}
//当前的深度是d ,开始的分母的值最小为from 余下的和为aa / bb
bool dfs(ll d , ll from ,ll aa , ll bb)
{
if (d == maxn ) {
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 (ll i = from ; ; i++) {
//剪枝--这个剪枝相当重要,没有的画程序会一直跑,而没有结果。原因是程序不知道后面的i不会产生我们想要的结果
if(bb * (maxn -d + 1) <= i *aa)
{ break ;}
v[d] = i ;
ll b2 = bb * i ;
ll a2 = aa * i - bb ;
ll g = gcd(a2 , b2) ;
if (dfs(d + 1, i + 1, a2/g, b2/g)) {
ok = true ;
}
}
return ok ;
}
int main(int argc, const char * argv[]) {
ll a , b ;
while (scanf("%lld%lld" , &a , &b)==2 && a + b != 0 ) {
int ok = 0 ;
for (maxn = 1; ; maxn++) {
memset(ans, -1, sizeof(ans)) ;
if (dfs(0, get_first(a, b), a, b)) {
ok = 1 ;
break ;
}
}
printf("%lld/%lld =" , a, b ) ;
int i = 0 ;
for ( i = 0; ; i++) {
if (ans[i+1] == -1) {
break ;
}
else{
printf(" 1/%lld + " , ans[i]);
}
}
printf(" 1/%lld\n" ,ans[i]) ;
}
return 0;
}