uva 12558 Egyptian Fractions (HARD version)

原题:
Given a fraction a/b, write it as a sum of different Egyptian fraction. For example, 2/3 = 1/2 + 1/6. There is one restriction though: there are k restricted integers that should not be used as a denominator. For example, if we can’t use 2..6, the best solution is:
2/3 = 1/7 + 1/9 + 1/10 + 1/12 + 1/14 + 1/15 + 1/18 + 1/28 The number of terms should be minimized, and then the large denominator should be minimized. If there are several solutions, the second largest denominator should be minimized etc.
Input
The first line contains the number of test cases T (T ≤ 100). Each test case begins with three integers a, b, k (2 ≤ a < b ≤ 876, 0 ≤ k ≤ 5, gcd(a,b) = 1). The next line contains k different positive integers not greater than 1000.
Output
For each test case, print the optimal solution, formatted as below. Extremely Important Notes It’s not difficult to see some inputs are harder than others. For example, these inputs are very hard input for every program I have:
596/829=1/2+1/5+1/54+1/4145+1/7461+1/22383
265/743=1/3+1/44+1/2972+1/4458+1/24519
181/797=1/7+1/12+1/2391+1/3188+1/5579
616/863=1/2+1/5+1/80+1/863+1/13808+1/17260
22/811=1/60+1/100+1/2433+1/20275
732/733=1/2+1/3+1/7+1/45+1/7330+1/20524+1/26388
However, I don’t want to give up this problem due to those hard inputs, so I’d like to restrict the input to “easier” inputs only. I know that it’s not a perfect problem, but it’s true that you can still have fun and learn something, isn’t it?
Some tips:
1. Watch out for floating-point errors if you use double to store intermediate result. We didn’t use double.
2. Watch out for arithmetic overflows if you use integers to store intermediate result. We carefully checked our programs for that.

Sample Input
5
2 3 0
19 45 0
2 3 1 2
5 121 0
5 121 1 33
Sample Output
Case 1: 2/3=1/2+1/6
Case 2: 19/45=1/5+1/6+1/18
Case 3: 2/3=1/3+1/4+1/12
Case 4: 5/121=1/33+1/121+1/363
Case 5: 5/121=1/45+1/55+1/1089

中文:
给你一个分数,让你写成埃及分数的形式,就是让你把这个分数写成所有分子为1的分数的和,要求使用的分数越少越好。其中有约束条件,有k个数字不可以做分母。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=10000;
ll maxd,a,b,k;
ll ans[maxn],v[maxn],rest[6];
ll gcd(ll aa,ll bb)//最大公约数,通分使用
{
    if(aa%bb==0)
        return bb;
    return gcd(bb,aa%bb);
}
ll get_first(ll aa,ll bb)//找到1/c≤a/b时最小的c
{
    int c=1;
    while(bb>aa*c)
        c++;
    return c;
}
bool better(ll d)//ans保存之前搜索到的解,v保存当前搜索到的解
{
    for(ll i=d;i>=0;i--)
        if(v[i]!=ans[i])
            return ans[i]==-1||v[i]<ans[i];//ans等于-1是之前没有这个解,v小于ans是找到了一个分母更小的解
    return false;
}
bool restrict(ll x)//限制条件,有些数字不能做分母
{
    if(k==0)
        return false;
    for(int i=1;i<=k;i++)
    if(x==rest[i])
    return true;
    return false;
}
bool dfs(ll d,ll from,ll aa,ll bb)
{
    if(d==maxd)//搜索深度达到d
    {
        if(bb%aa)//如果不能换成分子为1的形式,则不合理
            return false;
        v[d]=bb/aa;//保存结果
        if(restrict(bb/aa))//查看限制条件
            return false;
        if(better(d))//如果当前解更有,更新
            memcpy(ans,v,sizeof(ll)*(d+1));
        return true;
    }
    bool ok=false;
    from=max(from,get_first(aa,bb));//当前最小可以得到的分数
    for(int i=from;;i++)
    {
        if(restrict(i))
            continue;
        if(bb*(maxd+1-d)<=i*aa)//剪枝:如果剩下的maxd+1-d个分数全部都是1/i,加起来仍然不超过aa/bb,则无解
            break;
        v[d]=i;
        //计算aa/bb - 1/i,设结果为a2/b2
        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()
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    for(int j=1;j<=t;j++)
    {
        cin>>a>>b>>k;
        for(int i=1;i<=k;i++)
            cin>>rest[i];
        memset(v,-1,sizeof(v));
        for(maxd=1;;maxd++)
        {
            memset(ans,-1,sizeof(ans));
            if(dfs(0,get_first(a,b),a,b))
                break;
        }
        cout<<"Case "<<j<<": "<<a<<"/"<<b<<'=';
        for(int i=0;i<=maxd;i++)
        {
            if(i!=0)
                cout<<'+';
            cout<<1<<"/"<<ans[i];
        }
        cout<<endl;
    }
    return 0;
}

解答:
书上的例题,采用迭代加深搜索求解,经常用于求解深度没有上限的问题。每次枚举最大搜索深度maxd(此题深度也没有上限,但是一定有解)。所以需要用乐观估价函数来进行剪枝加速,个人理解就是目前得到的部分解到完整解的距离如果超过深度上限就剪枝,借用书上的话就是。
当前搜索到19/45=1/5+1/100+…,则后面的分数每个最大为1/101,至少需要(19/45-1/5) / (1/101) =23项总和才能达到19/45,因此前22次迭代是根本不会考虑这棵子树的。这里的关键在于:可以估计至少还要多少步才能出解。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值