UVa 12558 Egyptian Fractions (HARD version) Egyptian Fractions (hard version) IDA iterative deepening search

Question link: Egyptian Fractions (HARD version)
Question description:

Given a fraction, you need to split it into multiple molecules of 1 1To add the fractions of 1 , it is required that the fractions appearing cannot be repeated, andkkk numbers. These numbers cannot appear in the denominator. You need to output the denominator from small to large. If there are multiple solutions that meet the conditions, you need to output the solution with the smallest number of disassembled fractions as possible. If there are multiple solutions , you need to output the smallest score as large as possible. If there are still multiple solutions, you need to make the second smallest score as large as possible, and so on. The question ensures that the input data is not particularly difficult to decompose.
For example:
Insert image description here
whereCase 5 Case 5C a se 5 cannot use the number33 33Solution at 33 hours.

answer:

First of all, this question cannot use floating point numbers to calculate fractions, otherwise the accuracy will not be enough (it is clearly stated in the original question). Then we can use IDA IDA for this questionI D A answers, that is, iteratively deepens the search. Then we enumerate the possible values ​​​​of the current denominator, and then search. When we search, we can bring the status to indicate the current fraction that needs to be composed. When searching for the next level, use the current fraction minus the current enumerated fraction as the next level. The score the layer needs to search for. This process requires the use ofGCD GCDThe GC D algorithm (euclidean division method) is used for reduction.
The only problem that needs to be solved now is the upper and lower bounds of the enumeration denominator.
Let's first consider the lower bound of the denominator. First, in order to prevent repeated enumeration, we need to make the denominator of the next level larger than the denominator of the previous level. In this case, a lower limit of the denominator is the enumeration value of the previous level plus one. At the same time We also need to ensure that the current enumerated fraction is smaller than the fraction that needs to be composed, because the question requires that negative numbers cannot appear. If the fraction that currently needs to be enumerated isab \frac abbaWe set the current minimum value of the denominator to be ccc Then there is,ab ≥ 1 c \frac ab \ge \frac 1 cbac1After sorting, we can get c ≥ bac\ge \frac bacabSince it must be an integer we need to round up the right side of the inequality so that the lower bound can be determined as the maximum of the above two values.
Next is the upper bound of the denominator. First of all, we can easily think that the current upper bound cannot exceed the maximum value of the denominator of the currently recorded answer, because if it exceeds this value, then the currently recorded answer is better, so we do not continue. Searching is necessary, but the upper bound above can only be used when an answer is searched. So how should the upper bound be determined if no answer is found? We remember that there is nnn scores need to be searched, then forab \frac abbaWe make the score of the current position as small as possible, and since the subsequent scores must be smaller than the current position, we can assume that the subsequent scores are equal to determine the upper bound of the denominator search for the current position (if it is larger than this, then the subsequent scores Only when the denominator is smaller than the current denominator can it form the currently required fraction, which causes repeated searches), that is, nba \frac {nb} aanbHere we need to round down.

Code:

#include <bits/stdc++.h>

const int MAX_C = 1000;

using namespace std;

vector<long long> denominator;
vector<long long> ans;
int a, b, k, caseID, T, c, maxDepth;
bool ok[MAX_C + 1];
bool done;

long long gcd(long long a, long long b)
{
    
    
    return b == 0 ? a : gcd(b, a % b);
}

void dfs(int nowDepth, long long a, long long b)
{
    
    
    if (nowDepth == maxDepth) {
    
    
        if (a == 0) {
    
    
            if (ans.size() == 0) {
    
    
                ans = denominator;
            } else {
    
    
                bool needUpdate = false;
                for (int i = nowDepth - 1; i >= 0; i--) {
    
    
                    if (denominator[i] > ans[i]) {
    
    
                        break;
                    } else if (denominator[i] < ans[i]) {
    
     // 当前答案的最小的分数更加大(分母小的分数更大)或者第二小的更大...
                        needUpdate = true;
                        break;
                    }
                }
                if (needUpdate) {
    
     ans = denominator; }
            }
            done = true;
        }
        return;
    }
    long long nowDenomitor = b / a; // 当前的分数要比a/b小,那么当前的分母最小为b/a
    long long maxNowDenominator = (maxDepth - nowDepth) * b / a; // 由于后续的分数的分母要比当前选择的分母大,那么当前的分母最大值不能超过后续分母全部相等的情况
    if (b % a != 0) {
    
     nowDenomitor++; }
    if (nowDepth != 0) {
    
     nowDenomitor = max(nowDenomitor, denominator[nowDepth - 1] + 1); }
    if (ans.size() != 0) {
    
     maxNowDenominator = min(maxNowDenominator, ans[ans.size() - 1]); } // 上限不应该比当前记录的最小的分数还要大,刚好是最后一个的时候,可能会相等
    for (; nowDenomitor <= maxNowDenominator; nowDenomitor++) {
    
    
        if (nowDenomitor <= 1000 && !ok[nowDenomitor]) {
    
     continue; }
        denominator.push_back(nowDenomitor);
        long long na = a * nowDenomitor - b;
        long long nb = b * nowDenomitor;
        long long g = gcd(na, nb);
        dfs(nowDepth + 1, na / g, nb / g);
        denominator.pop_back();
    }
}

int main()
{
    
    
    ios::sync_with_stdio(false);
    cin >> T;
    while (T--) {
    
    
        done = false;
        denominator.resize(0);
        ans.resize(0);
        memset(ok, 1, sizeof(ok));
        caseID++;
        cin >> a >> b >> k;
        for (int i = 0; i < k; i++) {
    
    
            cin >> c;
            ok[c] = false;
        }
        for (maxDepth = 1; ; maxDepth++) {
    
    
            dfs(0, a, b);
            if (done) {
    
     break; }
        }
        cout << "Case " << caseID << ": ";
        cout << a << "/" << b << "=";
        for (size_t i = 0; i < ans.size(); i++) {
    
    
            if (i != 0) {
    
     cout << "+"; }
            cout << "1" << "/" << ans[i];
        }
        cout << endl;
    }
    return 0;
}

Guess you like

Origin blog.csdn.net/qq_45523675/article/details/129172082