Think About It: Corsa Contro il Tempo 2

Editorial

Yo, dopo una piccola attesa (:new_moon_with_face:) ecco l’editorial del problema.
Come al solito procediamo per subtask!

Subtask 1

Il subtask 1 è relativo ai casi d’esempio. Basta restituire l’output desiderato :new_moon_with_face:

int speedrunna(int N, int K, int a, int b){
    if(N == 3)return 3;
    return 1156;
}

Time complexity: O(1)

Space complexity: O(1)

Subtask 2 & 3

I subtask 2 e 3 sono stati ideati per testare che la soluzione prodotta si calcoli effettivamente i dati dal formato indicato.

L’idea di base era far scopiazzare il codice da cct e adeguarlo all’input.

#include <bits/stdc++.h>

using namespace std;

int speedrunna(int N, int K, int a, int b);


int mul(int a, int b, int mod){
    return (a * b) % mod;
}

int fastPow(int b, int e, int mod){
    if(e == 0){
        return 1;
    }
    int ans = fastPow(mul(b, b, mod), e / 2, mod);
    if(e & 1){
        ans = mul(ans, b, mod);
    }
    return ans;
}

int getTime(int i, int a){
    return fastPow(a, i, 1000);
}

int getCharge(int i, int b, int k){
    return fastPow(b, i, k);
}

int add(int a, int b){
    if(a == INT_MAX || b == INT_MAX)return INT_MAX;
    return a + b;
}

int dp(int i, int k, int a, int b, vector<vector<int>> &memo, int K){
    if(i == (int) memo[0].size()){
        return 0;
    }
    int &ret = memo[k][i];
    if(ret != INT_MAX){
        return ret;
    }
    int time = getTime(i, a);
    int charge = getCharge(i, b, K);
    if(k == K){
        ret = dp(i + 1, 0, a, b, memo, K);
    }
    ret = min(ret, time + dp(i + 1, min(K, k + charge), a, b, memo, K));
    return ret;
}

int speedrunna(int N, int K, int a, int b){
    vector<vector<int>> memo(K + 1, vector<int>(N, INT_MAX));
    return dp(0, 0, a, b, memo, K);
}

Time complexity: O(N \times K \times log(N))

Si può ridurre la complessità temporale calcolando una volta sola i valori delle cariche e dei tempi richiesti. Lo spazio di memoria a disposizione permette la memorizzazione di questi calcoli.

Space complexity: O(N \times K)

Subtask 4

Il subtask 4 è effettivamente il subtask in cui il problema si differenza dalla sua prima versione.
I limiti di memoria non ci permettono di memorizzare completamente la matrice per tutti gli stati possibili.

Con una soluzione top-down non è intuitivo a colpo d’occhio capire le dipendenze delle nostre transazioni.
Riscrivendo il codice con una soluzione bottom-up è facile notare che i K stati per i-esimo livello dipendono solo dai K del livello i - 1.

Per cui, tenendo i memoria solo i 2K stati interessati riduciamo la complessità spaziale da N \times K a K.

Uno sketch della tabella dei risultati intermedi per il test case di cct 1:


#include <bits/stdc++.h>

using namespace std;

int speedrunna(int N, int K, int a, int b);

int mul(int a, int b, int mod){
    return (a * b) % mod;
}

int fastPow(int b, int e, int mod){
    if(e == 0){
        return 1;
    }
    int ans = fastPow(mul(b, b, mod), e / 2, mod);
    if(e & 1){
        ans = mul(ans, b, mod);
    }
    return ans;
}

int getTime(int i, int a){
    return fastPow(a, i, 1000);
}

int getCharge(int i, int b, int k){
    return fastPow(b, i, k);
}

int add(int a, int b){
    if(a == INT_MAX || b == INT_MAX)return INT_MAX;
    return a + b;
}

int speedrunna(int N, int K, int a, int b){
    // initiating values first 
    vector<int> prev(K + 1, INT_MAX), cur(K + 1);
    prev[0] = 0;
    for(int i = 0; i < N; i++){
        fill(cur.begin(), cur.end(), INT_MAX);
        int time = getTime(i, a);
        int charge = getCharge(i, b, K);
        // Just completing the level
        for(int k = 0; k <= K; k++){
            int index = min(k + charge, K);
            cur[index] = min(cur[index], add(prev[k], time)); 
        }
        // using the strument
        cur[0] = min(cur[0], prev[K]);
        swap(prev, cur);
    }
    return *min_element(prev.begin(), prev.end());
}

Time Complexity: O(N \times K + N log(N) )

Si può ridurre la complessità computando passo per passo le potenze se si processano i livelli dal primo.
Nella soluzione proposta è possibile farlo, rimuovendo cosi il fattore N log (N)

Space Complexity: O(K)

Robe a caso

Il codici proposti nell’editorial sono stati sviluppati da me dopo aver programmato in java per un anno. Sono inutilmente prolissi per questa ragione :sweat:

1 Mi Piace