Visitare i punti su una linea numerica riducendo al minimo un costo non correlato alla distanza


18

Ho bisogno di aiuto su questo problema ACPC ICPC. La mia idea attuale è quella di modellare questo come un problema di percorso più breve, che è descritto nella dichiarazione del problema.

Problema

Esistono N = 1000contenitori per rifiuti nucleari situati lungo una linea numerica 1-D in posizioni distinte da -500,000 to 500,000, eccetto x=0. Una persona ha il compito di raccogliere tutti i cassonetti dei rifiuti. Ogni secondo in cui un contenitore per rifiuti non viene raccolto, emette 1 unità di radiazione. La persona inizia alle x = 0e può spostare l' 1unità ogni secondo, e la raccolta dei rifiuti richiede una quantità trascurabile di tempo. Vogliamo trovare la quantità minima di radiazione emessa durante la raccolta di tutti i contenitori.

Input di esempio:

4Contenitori situati a [-12, -2, 3, 7].

L'ordine migliore per raccogliere questi contenitori è [-2, 3, 7, -12], per un minimo di emissione di 50unità. Spiegazione: la persona va -2in 2 secondi e durante quel tempo 2 unitsdi emissione di radiazioni. Quindi va a 3(distanza:) in 5modo che il barile abbia rilasciato 2 + 5 = 7unità di radiazione. Ci vogliono 4più secondi per arrivare a x = 7dove quel barile ha emesso 2 + 5 + 4 = 11unità. Ci vogliono 19pochi secondi per arrivare a x = -12dove quel barile ha emesso 2 + 5 + 4 + 19 = 30unità. 2 + 7 + 11 + 30 = 50, che è la risposta.

Appunti

C'è una O(N!)soluzione ovvia . Tuttavia, ho esplorato metodi avidi come passare al più vicino o passare al cluster più vicino ma quelli non hanno funzionato.

Ho pensato a questo problema per un bel po 'e l'ho modellato come un problema di ricerca grafica:

  1. Inseriamo 0come posizione di base (questo sarà lo stato iniziale)
  2. Quindi, ordiniamo le posizioni dal meno al più grande.
  3. Facciamo quindi un BFS / PFS, in cui è statecomposto
    • Due interi le rche rappresentano un intervallo contiguo nella matrice posizione ordinato che abbiamo visitato già
    • Un numero intero locche ci dice se siamo sull'estremità sinistra o destra dell'intervallo
    • Un numero intero timeche ci dice il tempo trascorso
    • Un 'costo' intero che ci dice il costo totale finora (basato sui nodi che abbiamo visitato)
  4. Da ogni stato possiamo passare a [l - 1, r] e [l, r + 1], modificando di conseguenza gli altri 3 numeri interi
  5. Lo stato finale è [0, N], controllando entrambe le posizioni finali.

Tuttavia, sembra che [L, R, loc]non definisca in modo univoco uno stato e dobbiamo archiviarlo L, R, loc, and time, riducendo costal minimo in ciascuno di questi. Questo porta ad un algoritmo esponenziale, che è ancora troppo lento per qualsiasi bene.

Qualcuno può aiutarmi a espandere la mia idea o spingermi nella giusta direzione?

Modifica: forse questo può essere modellato come un problema di ottimizzazione della programmazione dinamica? Pensandoci, ha gli stessi problemi della soluzione di ricerca del grafico - solo perché la corrente costè bassa non significa che sia la risposta ottimale per quel sotto-problema, poiché timeinfluisce anche notevolmente sulla risposta.

Greedy non funziona: ho un algoritmo di selezione avido che stima il costo del trasferimento in un determinato luogo (ad es. Se ci spostiamo a destra, raddoppiamo le distanze ai barili di sinistra e simili).

Potresti fare una ricerca prioritaria, con un'euristica? L'euristico potrebbe combinare il costo del viaggio corrente con la quantità di tempo trascorso.


Che ne dici di algoritmi per percorsi più brevi? Come l'algoritmo di Dijkstra?
suraj_fale

Ci ho provato, ma penso di fare qualcosa di veramente sbagliato. Ho descritto il mio algoritmo (che era la prima ricerca prioritaria o BFS) in fondo, con l'elenco numerato.
Barron W.

Questo potrebbe aiutare a ... stackoverflow.com/q/14639346/585398
suraj_fale

Spiacenti, non vedo come questi due problemi siano correlati. Puoi spiegare?
Barron W.

2
Questo è un problema di pratica ACPC ICPC, non un problema di vita reale. In un'altra nota, ho provato a ridurre lo stato ma senza risultati. Ho provato a scrivere una soluzione DP, ma neanche quello ha funzionato. Lo stato era L, R, POS.
Barron W.

Risposte:


4

Penso che tu possa migliorare questo aspetto osservando il problema come un grafico diretto di coppie di posizioni.

Per questo esempio userò la linea con i valori -9, -6, -1, 3 e 5.

Poiché è troppo difficile disegnare un grafico con solo testo, rappresenterò le coppie come una tabella. Possiamo pensare alle celle come a rappresentare lo stato in cui sono stati raccolti tutti i contenitori tra quelle due posizioni. Ogni cella ha due valori, uno che rappresenta il costo per andare a sinistra, l'altro che rappresenta il costo per andare a destra (al contenitore successivo).

I valori possono essere semplicemente calcolati come la distanza tra i due punti moltiplicata per il numero di barili all'esterno di quei due punti + 1 . Le celle in cui entrambi i numeri hanno lo stesso segno rappresentano i casi in cui sono stati raccolti tutti i barili del segno opposto. Questi sono calcolati usando solo il numero di barili nella direzione lontana da zero.

Nella tabella i valori di X indicano che non puoi andare in quella direzione (sono stati presi tutti i barili in quella direzione). Le righe rappresentano la posizione corrente del collettore e le colonne rappresentano la posizione del barilotto opposto successivo.

    +------+------+------+------+------+
    |  -9  |  -6  |  -1  |   3  |   5  |
+---+------+------+------+------+------+
|-9 |      |      |      | X, 24| X, 14|
+---+------+------+------+------+------+
|-6 | 3, X |      |      | 9, 27| 6, 22|
+---+------+------+------+------+------+
|-1 |      |10, X |      |20, 8 |15, 18|
+---+------+------+------+------+------+
| 3 |24, 4 |27, 6 |16, 8 |      | X, 2 |
+---+------+------+------+------+------+
| 5 |14, X |22, X |18, X |      |      |
+---+------+------+------+------+------+

Le regole per spostarsi tra i quadrati possono essere alquanto confuse.

Per le colonne numerate negative, le regole sono le seguenti:

  • Andando a destra si sposta in basso di una cella.
  • Andando a sinistra, si sposta verso il basso di una cella, quindi si specchia sulla diagonale.
  • Se il valore giusto è X, andare a sinistra si sposta in alto verso la diagonale (dove colonna e riga sono uguali) e lasciato di uno.

Per le colonne numerate positive, le regole sono le seguenti:

  • Andando a sinistra si sposta in alto di una cella.
  • Andando a destra si sposta in alto di una cella e quindi si specchia sulla diagonale.
  • Se il valore a sinistra è X, andando a destra si sposta in basso sulla diagonale e a destra di uno.

Ora possiamo eseguire l'algoritmo di Dijkstra per calcolare il percorso migliore, usando queste regole di movimento per attraversare il grafico. Le nostre posizioni di partenza sono (-1, 3) e (3, -1) con costi iniziali di 5 e 15, rispettivamente. Una volta calcolati i valori per le due posizioni finali (sinistra di (-9, -9) e destra di (5, 5)), la più piccola delle due è la nostra risposta.

Il tempo di esecuzione per ciascuno dei passaggi è:

  • O (N log N) per ordinare inizialmente i valori di input lungo la linea
  • O (N ^ 2) per il calcolo della tabella / grafico
  • O (N ^ 2 log N) per eseguire Dijkstra sul grafico (Nota: ci sono al massimo due spigoli per ogni dato vertice).

I primi due passaggi sono dominati dall'ultimo, quindi il tuo runtime complessivo è O (N ^ 2 log N) che dovrebbe essere un runtime abbastanza buono per la sfida.


1

Distanza più breve

Ieri ho scritto un'applicazione Java per risolvere il problema. Il problema è sostanzialmente un problema di distanza più breve, come ha affermato SRJ nel suo commento. La radiazione mostra solo che è possibile accumulare un valore insieme alla distanza più breve.

Fondamentalmente, ecco cosa ho fatto.

  • Inserire i numeri del contenitore in un Elenco <Intero>
  • Mentre l'elenco contiene elementi;
    • Trova gli elementi più vicini
    • Se c'è un elemento, cammina lì e rimuovi l'elemento.
    • Se ci sono due elementi, copia il percorso e cammina verso entrambi gli elementi
  • Trova il percorso con il valore di radiazione più piccolo.

Ecco alcuni output dall'applicazione

10 containers are located at [-90, -75, -47, -9, 9, 26, 48, 50, 64, 79].

You walk to position -9 and pick up the container.  The total radiation emitted is 90 units.
You walk to position 9 and pick up the container.  The total radiation emitted is 252 units.
You walk to position 26 and pick up the container.  The total radiation emitted is 388 units.
You walk to position 48 and pick up the container.  The total radiation emitted is 542 units.
You walk to position 50 and pick up the container.  The total radiation emitted is 554 units.
You walk to position 64 and pick up the container.  The total radiation emitted is 624 units.
You walk to position 79 and pick up the container.  The total radiation emitted is 684 units.
You walk to position -47 and pick up the container.  The total radiation emitted is 1,062 units.
You walk to position -75 and pick up the container.  The total radiation emitted is 1,118 units.
You walk to position -90 and pick up the container.  The total radiation emitted is 1,133 units.

Non ho ottimizzato l'algoritmo in alcun modo. Non ho nemmeno notato che l'elenco di input dei numeri è stato ordinato. (Sono un doofus.)

Quando ho eseguito il mio codice con i valori massimi, 1.000 container e un intervallo compreso tra -500.000 e 500.000, il mio codice ha impiegato 3 secondi per essere eseguito. Il più delle volte scriveva le 1.000 righe di stampa sulla console.

Non sono una grande O persona, ma penso che la mia forza bruta che percorra l'algoritmo del percorso più breve sia O (N al quadrato), non O (N!).

Se avessi approfittato del fatto che i numeri di input sono ordinati, in modo da dover solo controllare i due numeri su entrambi i lati di dove mi trovo attualmente, potrei avvicinare l'applicazione a O (N). Devo solo controllare 2 numeri perché sto rimuovendo gli elementi dall'elenco man mano che li raggiungo.

Hai usato algoritmi diversi, come l'algoritmo goloso, per trovare una soluzione approssimativa.

Se il mio programma fosse durato 3 ore anziché 3 secondi, allora avresti una scelta da fare.

La soluzione abbastanza buona è abbastanza buona?

In altre parole, sono disposto a scambiare la velocità di elaborazione per una risposta abbastanza buona?

Se una risposta abbastanza buona è abbastanza buona, allora usi gli algoritmi di approssimazione.

Se vuoi la risposta perfetta, devi percorrere tutti i percorsi più brevi. Non ci sono scorciatoie.

Se qualcuno vuole che pubblichi il mio codice, lo farò. Sono sicuro che ci sono ancora bug, come volevo vedere se potevo implementare un algoritmo di camminata più breve.


1

Ho una soluzione che risolverà questo problema in 2^Ntempo, che è scarsa, ma penso che sia un modo utile per inquadrare il problema, quindi ho pensato di pubblicare.

Invece di modellare il problema come un grafico, modellerei un albero decisionale binario (diciamo T). Ad ogni livello devi scegliere tra andare a destra o a sinistra. È abbastanza facile calcolare il "costo" di ciascun bordo. Lascia che h(K)sia l'altezza del nodo corrente K, quindi il costo del bordo che va left_child(K) = h(K) x dist(K, left_child(K)). Un calcolo simile è sufficiente per il costo del margine per il figlio giusto. Costruisci questo albero e tieni traccia del costo cumulativo dei bordi fino in fondo, e segnala il percorso al nodo foglia con il costo totale più piccolo.

Si noti che il calcolo dei costi funziona perché la lunghezza di ciascun bordo dist(K, left_child(K))rappresenta il tempo necessario per passare al sito successivo, mentre l'altezza della sottostruttura è il numero di siti rimanenti (ad esempio, che emettono ancora radiazioni).

Ora il trucco all'interno di questo framework è determinare se ci sono alcune euristiche che puoi usare per 'dimostrare' che puoi ignorare l'espansione della ricerca lungo un ramo. La mia intuizione è che per ogni euristica del genere esiste una disposizione di siti che la sconfiggerà, ma forse qualcuno può inventare qualcosa.

Un certo numero ha proposto di applicare soluzioni di percorsi più brevi per i grafici, ho alcuni dubbi sul fatto che una soluzione di questo tipo possa funzionare. I tuoi vicini nel "grafico" nel problema cambieranno a seconda del percorso che segui. Ad esempio nel tuo post originale con [-12, -2, 3, 7]se vai a -2allora -12e 3diventi 'vicini' e se vai a 3allora -2e 7sei vicini. Ogni possibile "coppia" di valori positivi e negativi può essere potenzialmente un neigbore nel grafico finale. Non conosco alcun algoritmo di percorso più breve che sia dimostrabilmente corretto in un grafico dinamico.


0

Penso che abbia più senso pensare a ogni fase semplicemente come una scelta binaria tra andare a destra al barile più vicino e andare a sinistra al barile più vicino. Basta avere una funzione di costo che dettaglia il numero di unità di radiazione che verrebbero sostenute in totale facendo qualsiasi movimento e scegliere quella con il costo più basso.

NON considerare semplicemente il barile più vicino, ma supponi invece che allontanandoti da un barile stai effettivamente aggiungendo il doppio della radiazione perché allontanandoti hai anche sostenuto il costo di dover tornare indietro in quella direzione in seguito.

Nel tuo esempio di [-12, -2,3,7], lo spostamento a sinistra comporterebbe un totale di 14 (2 + 2 + 10) a sinistra e 18 (2 + 2 + 5 + 9) a destra, per un totale di 22. Lo spostamento a destra comporterebbe 10 (3 + 3 + 4) a destra e 26 (3 + 3 + 5 + 15) a destra. Chiaramente a sinistra è la soluzione giusta all'inizio. Un calcolo simile può essere fatto per ogni movimento successivo.

Dopodiché il problema si riduce sostanzialmente a una ricerca, quindi la complessità dovrebbe essere O (nlog (n)), che è MOLTO migliore di O (n!). Credo che questa sia necessariamente la più bassa complessità che può esistere per questo problema poiché è fondamentalmente un algoritmo di ricerca basato sul confronto per il quale non è possibile fare meglio di O (nlog (n))

Apparentemente non ero abbastanza chiaro con questa descrizione, quindi ho deciso di renderlo un po 'più programmatico: 1. Calcola il costo sostenuto andando a sinistra e il costo sostenuto andando a destra in base alla posizione corrente 2. Spostati in la direzione meno costosa 3. Rimuovere la canna raggiunta dalla considerazione nel calcolo del costo di spostamento in una direzione

Calcolo del costo: 1. Identificare il barilotto più vicino nella direzione indicata. Diciamo che $ dist è la distanza dalla posizione corrente al barilotto più vicino nella direzione data. 2. Il costo viene inizializzato come N * $ dist dove N considera solo i barili ancora attivi. 3. A questo, aggiungere la distanza che la nuova posizione indicata da $ dist sarebbe da ogni barile rimanente.


1
Questo non funziona sempre. Forse potresti ordinare i coords e quindi fare una ricerca in cui lo stato contiene un intervallo [i..j] (che indica quale intervallo hai visitato) e il costo e l'ora corrente.
Barron W.

Quando non funziona?
Slater Victoroff

Si è verificato un semplice caso di test in cui questo non è riuscito. Proverò a trovarlo, ma era con N = 4 o 5.
Barron W.

[43, -18, -98, -82, 63]
Barron W.

Anche casi come [-10,-11, 10,20,30,40,50,60,70]. La soluzione corretta e unica è quella di raccogliere tutti quelli negativi, quindi raccogliere quelli positivi. Per una risposta di 455.
Barron W.

0

Soluzione parziale - ci tornerò più tardi.

Supponiamo che la strategia "predefinita" sia eseguita a sinistra oa destra, a seconda di quale sia più economico. Ora chiedi, vale la pena fare una piccola gita dall'altra parte per prendere un barile. È abbastanza facile calcolare la risposta.

Per l'input di esempio, correre fino in fondo a destra è più economico di tutto a sinistra. Vale la pena fare un viaggio laterale a -2? Riduce il costo della corsa fino in fondo e poi di nuovo a 0 per 14 (perché stavi "pagando" 4 unità di radiazione per mossa da 0 a 3 sulla strategia predefinita, ora è fino a 3, ne pagavi 3 da 3 a 7, ora è 2, ecc.), inoltre riduce di uno a mossa il costo del passaggio da 0 a -2, risparmiando così 2 in più per un totale di 16.

Tuttavia, aggiunge un costo di andare a -2, quindi di nuovo a 0 di 14 (4 unità per mossa a -2, 3 per mossa di nuovo a 0), per un guadagno netto di (16-14) = 2. Nota che per calcolare questo non devi valutare il costo esatto per risolvere l'intero problema per ogni decisione - hai abbastanza informazioni per decidere semplicemente sapendo se correre fino in fondo a sinistra è più economico che correre fino in fondo, oltre a come molti contenitori per rifiuti sono su ciascun lato di te e la distanza dal più vicino 2. Quindi questo è O (N ^ 2).

Fatta eccezione per un problema importante, ho pensato che correrai fino alla fine, e sappiamo che potresti raddoppiare. Per ripulirlo, dobbiamo aggiornare il mio calcolo. Per l'input del campione, ho supposto che avresti risparmiato 14 emettendo 1 radiazione totale in meno per unità al secondo durante l'esecuzione da 0 a 7 e ritorno. Ma, se doppi indietro prima di correre a 7, i risparmi saranno ridotti.

È piuttosto brutto, perché non so come calcolare il prossimo double-back senza provare tutte le possibilità, il che ci riporta a O (2 ^ N).

Tranne - è 2 ^ N con potatura. Ho calcolato che il "viaggio laterale" a -2 costa 14, ma ho guadagnato 16, se non avessi avuto più viaggi secondari prima di arrivare al numero più a destra. Se il numero più a destra fosse stato 5, avrei immediatamente capito che il viaggio laterale a -2 non avrebbe potuto pagare. (Costo ancora 14, beneficio massimo 12). Né devo considerare di andare a -2 e poi fare un viaggio laterale prima di raggiungere 6, dato che questo è sempre inferiore al solo andare dritto a quel punto in primo luogo.


0

Penso che tu possa risolverlo usando una prima ricerca, mantenendo non più di 2 * N ^ 2 tuple di (booleano, int, int, int, stringa) in cui le stringhe sono lunghe quanto il percorso è complicato.

Le tuple sono (minimo o massimo booleano, posizione minima percorsa, posizione massima percorsa, radiazione totale emessa, cronologia del percorso).

Vedo l'algoritmo andare così:

  1. Inizializza il pool di tuple in una singola voce, (min, 0, 0, 0, "")
  2. Trova un elemento nella piscina che abbia una radiazione minima emessa. Se min e max corrispondono al minimo e al massimo di tutti i barili, la cronologia del percorso è la soluzione ottimale. Altrimenti eliminalo dal pool.
  3. Calcola i 2 discendenti di questa tupla, ognuno dei quali corrisponde a camminare a sinistra o a destra verso la successiva botte non trattata.
  4. Inserisci i discendenti nel pool. Se esiste già un elemento nel pool con lo stesso valore booleano, min e max di un nuovo discendente, quindi scartare l'elemento con il conteggio di radiazioni più elevato.
  5. vai 2.

Trovare e rimuovere le tuple dominate migliorerà notevolmente le prestazioni. Potrebbe essere utile aggiungere una bandiera "ha generato" a ciascuna tupla e lasciare le tuple allevate nella piscina.

Ci sono anche alcune decisioni importanti da prendere nel decidere come conservare le tuple e cercarle per dominazioni e nuovi elementi da allevare.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.