UVA - 12558 Egyptian Fractions (HARD version) : 埃及分数问题 IDA*

题目点此跳转

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

思路

 题目意思是把a/b写成不同的埃及分数之和,要求项数尽量小,在此前提下最小的分数尽量大,然后第二小的分数尽量大……另外有k(0≤k≤5)个数不能用作分母。例如,k=0时5/121=1/33+1/121+1/363,不能使用33时最优解为5/121=1/45+1/55+1/1089。
 输入保证2≤a < b ≤ 876,gcd(a,b)=1,且会挑选比较容易求解的数据。
 说白了就是埃及分数问题,不过加了一个简单的限制:有一个最多5个数的限制使用表,表里的数不能作为分母,但是表里的数值不会超过1000.

  使用迭代加深(ID)算法,递增搜索深度,然后在当前确定的搜索深度下做普通的dfs即可.
  此题的关键在于bfs时要求下一层的上界和下界.
 下界: max(当前分母+1, 原分母/原分子向下取整)
 上界:当后面所有的数都为当前分母时还是小于左边的数,则后面不可能有结果(分母越来越大,值越来越小).

 注意点: 注意限制表的长度有限,大于1000的不用判断,需要直接取,如果也从限制表里取下标的话, 可能会数组越界; 中间结果相乘卡long long , 相除卡精度.

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <utility>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
#define OT freopen("out.txt", "w", stdout);
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int maxn = 1e3 + 10;
const int INF = 0x7fffffff;
const int dir[5][2] = {0,0,-1,0,1,0,0,-1,0,1};

LL a, b, k, c, r[maxn], maxd, st, ans[maxn], aid, res[maxn], rid = 0, step;

LL gcd(LL x, LL y) {
    if(y == 0) return x;
    return gcd(y, x%y);
}

bool dfs(LL x, LL y, LL deep) {
    if(deep == maxd) {
        if(x != 1 || (y <1100 && r[y] == 1)) return 0;
        ans[aid++] = y;
        int f = 0;
        for(LL i = aid-1; i >= 0; --i) {
            if(ans[i] == res[i]) continue;
            if(ans[i] < res[i] || res[i] == -1) { f = 1; break; }
            else break;
        }
        if(f==1) memcpy(res, ans, sizeof(res)), rid = aid;
        --aid; return 1;
    }
    bool ok = 0;
    st = max(st, (y-1)/x+1);
    for(LL i = st; ; ++i) {
        if(y*(maxd-deep+1) <= x*i) break;
        LL ta = x*i - y, tb = y*i, tc = gcd(ta, tb);
        ta /= tc; tb /= tc;
        if(i > 1000 || r[i] == 0) {
            ans[aid++] = i; LL tmp = st; st = i+1;
            if(dfs(ta, tb, deep+1)) ok = 1;
            --aid; st = tmp;
        }
    }
    return ok;

}

void IDAS() {
    maxd = 1; st = (b-1)/a+1;
    for(maxd = 1; ; ++maxd) {
        st = (b-1)/a+1; aid = 0; fill(res,res+maxn, INF); rid = 0;
        if(dfs(a, b, 0)) break;
    }
    printf("1/%lld", res[0]);
    for(LL i = 1; i < rid; ++i) printf("+1/%lld", res[i]); printf("\n");
}
int main() {
    #ifdef _LOCAL
    IN; //OT;
    #endif // _LOCAL

    int kase = 0, t; cin >> t;
    while(t--) {
        scanf("%lld%lld%lld", &a, &b, &k); met(r, 0);
        for(int i = 0; i < k; ++i) scanf("%lld", &c), r[c] = 1;
        printf("Case %lld: %lld/%lld=", ++kase, a, b);
        IDAS();
    }


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值