Credo che la seguente soluzione funzioni correttamente e utilizzi la memoria O (1), supponendo che sia possibile contenere un numero intero nello spazio O (1). L'idea è di provare a eseguire questo processo all'indietro fino a quando non trovi la posizione definitiva del pezzo di caramelle corretto. traccia
Let attraverso un esempio di questo problema in cui n = 10. Abbiamo poi ottenere questo:
1 2 3 4 5 6 7 8 9 10
X X X
2 3 5 6 7 8 10
X X
3 5 7 8 10
X X
5 7 10
X
7 10
X
10
Ora, supponiamo che vogliamo calcolare il risultato finale per questo problema. Sappiamo che quando abbiamo finito, le caramelle mangiate sono in posizione 1, dato che c'è rimasto solo un pezzo di caramella. Allora proviamo la sua creazione come questo:
1
Ora, sappiamo che sul precedente iterazione, la caramella di indice 1 deve essere stato mangiato. Ciò significa che la caramella finale era in realtà nella posizione 2 ultima volta:
? 2
Sulla ripetizione prima di questo, sappiamo che dal caramelle 1 è stato mangiato, la nostra caramelle deve effettivamente essere in posizione 3:
? ? 3
A questo punto, ripensiamo ancora a un'iterazione. Sappiamo che la caramella 1 è stata mangiata, ma anche le caramelle 4 sono state mangiate. Ciò significa che l'indice della nostra caramella deve essere stato 5 nella precedente iterazione, poiché quando lo scendiamo nella posizione corretta deve essere saltato indietro di un punto per il primo elemento e uno per il quarto elemento:
? ? ? ? 5
Ripetendo questa stessa logica, si ottiene che l'indice precedente sarebbe stato 7:
? ? ? ? ? ? 7
Ora, per il prossimo passo, sappiamo che avremmo scooted la caramella giù due posizioni perché abbiamo abbandonato il 1 ° e 4 ° elementi. Tuttavia, questo metterebbe le nostre caramelle in posizione 9, che sarebbe stata cancellata. Ciò significa che invece avremmo imbattiamo la caramella per posizionare 10:
? ? ? ? ? ? ? ? ? 10
A questo punto, dal momento che ci sono 10 caramelle a sinistra, sappiamo che abbiamo completamente invertito il processo e finito. Dato che il punto finale di riposo delle nostre caramelle era la posizione 10, sappiamo che la risposta è che la decima caramella è quella che è stata mangiata, il che corrisponde perfettamente al nostro precedente lavoro!
Il trucco dietro questo approccio è che non abbiamo bisogno di molta memoria per farlo funzionare. In particolare, ad ogni passo abbiamo bisogno di tenere traccia di solo poche cose:
- Qual indice è il pezzo finale di caramelle attualmente? Abbiamo bisogno di questo per sapere dove fermarsi.
- Quanti quadrati sono al di sotto di questo indice? Abbiamo bisogno di questo per sapere quanti elementi vengono eliminati su ogni passaggio.
- Qual è il prossimo quadrato perfetto? Abbiamo bisogno di questo per sapere quando il numero di quadrati che vengono cancellati ogni volta aumenta.
- Qual è stato l'ultimo indice che abbiamo esplorato? Questo algoritmo funziona eseguendo il processo all'indietro, quindi ad un certo punto ci accorgeremo che l'abbiamo eseguito una volta di più.Quando ciò accade, dobbiamo essere in grado di "eseguire il backup" di un passaggio per raggiungere l'ultimo indice che non ha superato il limite.
Detto questo, abbiamo il seguente algoritmo:
- Impostare l'indice corrente a 1.
- Impostare il numero di quadrati perfetti piccole per 1.
- Impostare il prossimo quadrato perfetto per 4.
- Impostare l'ultimo indice più piccolo per essere 1.
- Mentre l'indice corrente è inferiore a n:
01.235.
- Imposta l'ultimo indice più piccolo come indice corrente (ricorda la soluzione fino ad ora).
- impostare la corrente index + = il numero di quadrati perfetti piccoli (eseguire il processo indietro di un passo)
- Se l'indice corrente è uguale al successivo quadrato perfetto, aggiungere uno da esso (un caso limite di corsa all'indietro , se ci ha colpito un quadrato perfetto, dovremmo essere un passo passato)
- Se l'indice corrente è maggiore il prossimo quadrato perfetto (ora ci sono più numeri che sono cancellati ogni passo):
- Impostare il quadrato perfetto per essere il prossimo il quadrato perfetto.
- Aggiungi uno al numero di quadrati perfetti più piccoli dell'indice.
- Ritorna l'ultimo indice più piccolo.
Ciò richiede solo memoria O (1) per contenere tutti i valori.
Proviamo un esempio! Con n = 20, se lavoriamo attraverso il processo formale, otteniamo questo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
X X X X
2 3 5 6 7 8 10 11 12 13 14 15 17 18 19 20
X X X X
3 5 7 8 10 11 13 14 15 17 18 19
X X X
5 7 10 11 13 14 17 18 19
X X X
7 10 13 14 17 18
X X
10 13 17 18
X X
13 17
X
17
Se corriamo il nostro algoritmo, otteniamo
Current Index Next Square Smaller Squares
1 4 1
2 4 1
3 4 1
5 9 2
7 9 2
10 16 3
13 16 3
17 25 4
21 25 4
Dal 21> 20, l'ultimo indice più piccolo è 17 , quindi restituiamo 17, che è la risposta corretta!
scritto come codice C, presupponendo l'assenza di overflow integer:
int EatenCandyIndex(int n) {
int currIndex = 1;
int numSmallerSquares = 1;
/* Rather than tracking the next square, track the root of the next
* square. We can just square this to get the next square.
*/
int rootOfNextSquare = 2;
/* The last spot where the candy would be before we had Too Much Candy. */
int lastIndex = 1;
while (currIndex <= n) {
lastIndex = currIndex;
currIndex += numSmallerSquares;
if (currIndex == rootOfNextSquare * rootOfNextSquare)
++currIndex;
if (currIndex > rootOfNextSquare * rootOfNextSquare) {
++numSmallerSquares;
++rootOfNextSquare;
}
}
return lastIndex;
}
Tuttavia, come scritto, questo algoritmo non è particolarmente efficiente. In particolare, guarda il suo comportamento nell'esempio in cui n = 20. Si noti che abbiamo tre round in cui la dimensione del passo è uno, due con il passo due e tre, ecc. Invece di avere questi round si verificano esplicitamente, potremmo invece calcolare quanti round devono verificarsi con quella dimensione del passo, quindi esegui tutti questi passaggi contemporaneamente. In questo modo, abbiamo sempre un round con taglia uno, un giro con taglia due, un giro con taglia tre, ecc. Per fare ciò, ad ogni passo avremo bisogno di vedere qual è il nostro prossimo obiettivo; questo sarà il numero n o il prossimo quadrato perfetto. Una volta trovato il nostro obiettivo, dobbiamo vedere quanti passaggi sono necessari per arrivarci. Se l'indice corrente è io e il nostro obiettivo è t, e se la nostra dimensione del passo è k, allora dobbiamo prendere i passi ⌈ (t - i)/k ⌉ per arrivarci.Utilizzando un trucco carino con divisione intera, possiamo calcolare questo come
int numSteps = ((t - i) + (k - 1))/k;
Questo ci dà il seguente algoritmo aggiornato:
int EatenCandyIndexFaster(int n) {
int currIndex = 1;
int numSmallerSquares = 1;
/* Rather than tracking the next square, track the root of the next
* square. We can just square this to get the next square.
*/
int rootOfNextSquare = 2;
while (true) {
/* Figure out what our target is. */
int target = min(n, rootOfNextSquare * rootOfNextSquare);
/* See how many steps are required. */
int numSteps = ((target - currIndex) + (numSmallerSquares - 1))/numSmallerSquares;
/* See where we'd end up if we took one fewer than this many steps forward. */
int lastIndex = currIndex + (numSteps - 1) * numSmallerSquares;
/* Take that many steps forward. */
currIndex += numSmallerSquares * numSteps;
/* There is an edge case here: if we hit our number but it's a perfect square,
* we want to return the previous value.
*/
if (currIndex == n && n == rootOfNextSquare * rootOfNextSquare)
return lastIndex;
/* Otherwise, if we hit the target number exactly, return it. */
if (currIndex == n)
return currIndex;
/* Otherwise, if we overshot the target number, hand back where we'd be if we
* took one fewer step.
*/
if (currIndex > n)
return lastIndex;
/* Oh well; didn't make it. If we hit a perfect square, skip it. */
if (currIndex == rootOfNextSquare * rootOfNextSquare)
++currIndex;
++numSmallerSquares;
++rootOfNextSquare;
}
}
Questa versione ottimizzata dell'algoritmo viene eseguito in O (√ N) tempo e usa lo spazio O (1). Il motivo per cui il tempo è legato è che ogni passo dell'algoritmo passa al quadrato perfetto successivo e ci sono solo O (√ N) quadrati perfetti in meno di N.
Spero che questo aiuti!
Pls chiarire per quali valori di 'n' fallisce? –
@RajendranT per n> = 90000000 il mio codice non funziona a causa dell'heap della memoria. – Algorithmist
@kilotaras è il problema di Div 1. – Algorithmist