#UVA12588#Egyptian Fractions (HARD version)(ID_搜索基础题)

本文介绍了一种优化算法,用于将特定的真分数转换成不同埃及分数之和的形式,并在限制条件下寻找最优解。该算法考虑了分母数量最小化及分母值的优化排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

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 

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,让你把它拆分为一组分子为1的分数1/k i。多种可行解中,先选项数最小的,如果一样多,比较两组中分母最大的,选小的那个,一样多就在比较两组中第二大的,依次类推,求出最优解。

枚举拆分成多少个分数,然后去填,按照从小到大填。

注意每一个数字的搜索范围,比前一个大,比极值小。假设后面的数都平均分配。


Code:

Status Accepted
Time 910ms
Length 1942
Lang C++ 5.3.0
Submitted
Shared
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;

const int Max = 1000;
const LL INF = 0xffffffff;

int lim;
LL rt[Max], Ans[Max];
bool vis[Max + 5];

bool getint(int & num){
    char c; int flg = 1;    num = 0;
    while((c = getchar()) < '0' || c > '9'){
        if(c == '-')    flg = -1;
        if(c == -1) return 0;
    }
    while(c >= '0' && c <= '9'){
        num = num * 10 + c - 48;
        if((c = getchar()) == -1)   return 0;
    }
    num *= flg;
    return 1;
}

LL gcd(LL a, LL b){
    LL t = a % b;
    while(t){a = b, b = t, t = a % b;}
    return b;
}

bool flg;

bool Check(){
    for(int i = lim; i; -- i)   if(rt[i] != Ans[i])
        return rt[i] < Ans[i];
    return 0;
}

void Dfs(LL a, LL b, int now){
    if(now > lim)  return ;
    if(b % a == 0 && b / a > rt[now - 1] && (b / a > 1000 || ! vis[b / a])){
        rt[now] = b / a;
        if(! flg || Check())
            memcpy(Ans, rt, sizeof rt );
        flg = 1;
        return ;
    }
    LL dn = max(b / a, rt[now - 1] + 1);
    LL up = (lim - now + 1) * b / a;
    if(flg) up = min(up, Ans[lim] - 1);
    for(LL i = dn; i <= up && i <= INF / b; ++ i)if(i > 1000 || ! vis[i]){
        LL fac = gcd(a * i - b, b * i);
        rt[now] = i;
        Dfs((a * i - b) / fac, b * i / fac, now + 1);
    }
}

int main(){
    int T;
    getint(T);
    int a, b, n, k;
    for(int t = 1; t <= T; ++ t){
        flg = 0;
        getint(a), getint(b);
        getint(n);
        while(n --) getint(k), vis[k] = 1;
        for(lim = 1; ; ++ lim){
            Dfs(a, b, 1);
            if(flg)  break;
        }
        printf("Case %d: ", t);
        printf("%d/%d=1/%lld", a, b, Ans[1]);
        for(int i = 2; i <= lim; ++ i)
            printf("+1/%lld", Ans[i]);
        putchar(10);
        memset(vis, 0, sizeof vis );
        memset(Ans, 0, sizeof Ans );
    }
    return 0;
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值