UVa 12558 Fracciones egipcias (versión DIFÍCIL) Fracciones egipcias (versión difícil) Búsqueda de profundización iterativa IDA

Enlace de pregunta: Fracciones egipcias (versión DIFÍCIL)
Descripción de la pregunta:

Dada una fracción, debes dividirla en varias moléculas de 1 1Para sumar las fracciones de 1 se requiere que las fracciones que aparecen no se puedan repetir, ykkk números. Estos números no pueden aparecer en el denominador. Debe generar el denominador de pequeño a grande. Si hay varias soluciones que cumplen las condiciones, debe generar la solución con el menor número posible de fracciones desensambladas. Si hay Si hay múltiples soluciones, debe generar la puntuación más pequeña lo más grande posible. Si todavía hay múltiples soluciones, debe hacer que la segunda puntuación más pequeña sea lo más grande posible, y así sucesivamente. La pregunta garantiza que los datos de entrada no sean particularmente difíciles de descomponer.
Por ejemplo:
Insertar descripción de la imagen aquí
dondeCaso 5 Caso 5El caso 5 no puede utilizar el número33 33Solución a las 33 horas.

respuesta:

En primer lugar, esta pregunta no puede utilizar números de punto flotante para calcular fracciones; de lo contrario, la precisión no será suficiente (está claramente indicado en la pregunta original). Entonces podemos usar IDA IDA para esta pregunta.I D A responde, es decir, profundiza iterativamente en la búsqueda. Luego enumeramos los posibles valores del denominador actual y luego buscamos. Cuando buscamos, podemos traer el estado para indicar la fracción actual que debe componerse. Al buscar el siguiente nivel, use la fracción actual. menos la fracción enumerada actual como el siguiente nivel. La puntuación que la capa necesita buscar. Este proceso requiere el uso deGCD GCDPara la reducción se utiliza el algoritmo GC D (método de división euclidiana).
El único problema que debe resolverse ahora son los límites superior e inferior del denominador de enumeración.
Consideremos primero el límite inferior del denominador. Primero, para evitar enumeraciones repetidas, necesitamos hacer que el denominador del siguiente nivel sea mayor que el denominador del nivel anterior. En este caso, un límite inferior del denominador es el valor de enumeración del nivel anterior más uno. Al mismo tiempo, también debemos asegurarnos de que la fracción enumerada actual sea menor que la fracción que necesita componerse, porque la pregunta requiere que los números negativos no puedan aparecer. Si la fracción que actualmente necesita a enumerar esab \frac abbunEstablecemos el valor mínimo actual del denominador en ccc Entonces existe,ab ≥ 1 c \frac ab \ge \frac 1 cbunC1Después de ordenar, podemos obtener c ≥ bac\ge \frac baCasegundoComo debe ser un número entero, debemos redondear el lado derecho de la desigualdad para que el límite inferior pueda determinarse como el máximo de los dos valores anteriores.
El siguiente es el límite superior del denominador. En primer lugar, podemos pensar fácilmente que el límite superior actual no puede exceder el valor máximo del denominador de la respuesta registrada actualmente, porque si excede este valor, entonces la respuesta registrada actualmente es mejor. , por lo que no continuamos. La búsqueda es necesaria, pero el límite superior anterior sólo se puede utilizar cuando se busca una respuesta. Entonces, ¿cómo se debe determinar el límite superior si no se encuentra ninguna respuesta? Recordamos que hay nnEs necesario buscar n puntuaciones, luego ab \frac abbunHacemos que la puntuación de la posición actual sea lo más pequeña posible y, dado que las puntuaciones posteriores deben ser menores que la posición actual, podemos suponer que las puntuaciones posteriores son iguales para determinar el límite superior de la búsqueda del denominador para la posición actual (si es mayor que esto, entonces las puntuaciones posteriores. Solo cuando el denominador es menor que el denominador actual puede formar la fracción requerida actualmente, lo que provoca búsquedas repetidas), es decir, nba \frac {nb} aanótese bienAquí tenemos que redondear hacia abajo.

Código:

#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;
}

Supongo que te gusta

Origin blog.csdn.net/qq_45523675/article/details/129172082
Recomendado
Clasificación