算法竞赛入门经典--第7章,迭代加深搜索---埃及分数

题目大意见刘汝佳先生的算法竞赛入门经典第二版--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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值