brusegan_samuele_VETF04000T_secondaprova
Elaborato per la Gara Nazionale di Informatica (GNI) 2026 — ITIS Pininfarina, Moncalieri (TO).
- Autore: Brusegan Samuele
- Codice meccanografico:
VETF04000T - Prova: Seconda prova
Indice
Traccia
Il testo ufficiale della prova è un PDF di 5–6 pagine fornito dalla commissione GNI 2026 e non è ancora incluso in questo repository. Verrà aggiunto in docs/traccia.pdf appena disponibile.
Sintesi inferita dal codice
Dato un insieme di Punti di Interesse (POI), ciascuno caratterizzato da:
- un nome,
- un valore di prestigio (numero reale),
- un tipo (
0= culturale,1= panoramico — convenzione inferita dal dataset),
va costruito un itinerario turistico che:
- parte dall'ultimo POI del dataset, il Castello di Moncalieri (nodo fisso di partenza);
- visita un sottoinsieme degli altri POI in un certo ordine;
- massimizza il prestigio totale accumulato lungo il percorso.
Sul singolo passaggio fra due POI consecutivi viene applicato un bonus di varietà: se i due POI sono di tipo diverso (uno culturale e uno panoramico), il prestigio del POI di arrivo viene moltiplicato per un coefficiente MULTIPLIER = 1.5. Il primo POI del percorso contribuisce con il proprio prestigio "nudo".
Nel codice attuale sono fissati anche:
K = 4— numero di nodi che vengono lasciati "fuori" dall'esplorazione ricorsiva sul fondo dello stack (vedi Algoritmo);- partenza dal
Castello di Moncalieri, che viene rimosso dalla lista e ri-aggiunto in coda a tutti i path generati.
Quando avrò a disposizione il PDF della traccia, questa sezione verrà sostituita con il testo esatto e i vincoli ufficiali.
Modello del problema
Il problema è modellabile come ricerca della permutazione di massimo "prestigio" su un insieme di nodi etichettati, con una funzione di costo che dipende dall'ordine (per via del bonus di tipo) — è quindi una variante di problema NP-hard sullo spazio delle permutazioni, risolto qui per via esatta sfruttando la dimensione contenuta dell'istanza (≤ 20 POI nei dataset forniti).
Classi del dominio
-
Node(org.example.models.Node) POI immutabile. Campi:name : String,prestigio : double,type : boolean(true ↔ "panoramico"). Implementaequals/hashCodesu tutti i campi: è quindi usabile come chiave/elemento diHashSet/HashMap, sfruttato per la memoization. -
Path(org.example.models.Path) Sequenza ordinata diNodecon il proprioprestigioTotalecumulato. Espone:add(Node)— append in coda, aggiorna il prestigio in O(1) usando l'ultimo nodo come "predecessore";addAsFirstNode(Node)— insert in testa, ricalcola il prestigio scorrendo tutto il path (perché cambia il vicino sinistro di ciò che prima era in testa) — O(n);getPrestigio()— getter del prestigio cumulato;- copy-constructor
Path(Path other)— usato per copie difensive nella cache.
-
MainLogic(org.example.models.MainLogic) Orchestratore: importa i dati, lancia la ricorsione, sceglie il path migliore. Espone ancheneedBonus(Node s, Node e)come predicato del bonus di varietà. -
Importer(org.example.models.Importer) Lettore CSV "manuale" (split su,). Path hard-coded su./src/main/resources/punti_interesse.csv.
Diagramma classi
classDiagram
class Node {
-String name
-double prestigio
-boolean type
+Node(String, double, boolean)
+Node(String, double, int)
+getType() boolean
+getPrestigio() double
+equals(Object) boolean
+hashCode() int
+toString() String
}
class Path {
-ArrayList~Node~ nodes
-double prestigioTotale
+Path()
+Path(Path other)
+add(Node) void
+addAsFirstNode(Node) void
+getPrestigio() double
-calculateDeltaPrestigio(Node, Node) double
-updatePrestigio() void
}
class MainLogic {
+double MULTIPLIER$
+int K$
+double LENGHT$
-HashMap~Set~Node~,List~Path~~ cache$
+main(String[])$ void
+importData()$ ArrayList~Node~
+getPaths(List~Node~)$ ArrayList~Path~
+needBonus(Node, Node)$ boolean
}
class Importer {
-String FILENAME_CSV_DATA$
+importData()$ ArrayList~Node~
}
class PathBuilder {
+run() void
}
<<Runnable>> PathBuilder
class App {
+start(Stage) void
+main(String[])$ void
}
class MainUI {
+start(Stage)$ void
}
class Controller
Path "1" o-- "*" Node : contiene
MainLogic ..> Importer : usa
MainLogic ..> Path : crea/elabora
MainLogic ..> Node : usa
Importer ..> Node : istanzia
App --> MainUI : avvia
MainUI ..> Controller : (previsto)
Controller ..> MainLogic : (previsto)
PathBuilder ..> MainLogic : (previsto)
Algoritmo
Idea
Enumerazione delle permutazioni della lista dei POI con due ottimizzazioni:
- Memoization su sotto-istanze: due rami di ricorsione che arrivano allo stesso insieme (non sequenza!) di nodi rimanenti producono lo stesso insieme di sotto-path. La chiave della cache è quindi un
Set<Node>e non una lista ordinata. - Pruning sul fondo: la ricorsione si ferma quando restano
Knodi (conK = 4), restituendo un path vuoto. IKnodi residui non vengono permutati a quel livello — la logica completa di chi gestisce gli ultimiKnodi è parte dei TODO (vedi anche Note di correttezza).
Pseudocodice (vedi <ref_snippet file="/home/samuele/IdeaProjects/brusegan_samuele_VETF04000T_secondaprova/src/main/java/org/example/models/MainLogic.java" lines="46-83" />):
getPaths(nl):
key = set(nl)
if key in cache:
return deep_copy(cache[key])
if |nl| == LENGHT - K - 1:
return [ Path() ] # caso base
result = []
for n in nl:
sub = getPaths(nl \ {n}) # ricorsione su lista priva di n
for p in sub: p.addAsFirstNode(n) # n diventa la testa
result += sub
cache[key] = deep_copy(result) # copia difensiva
return result
main() poi:
nl = importData()
moncalieri = nl.removeLast()
paths = getPaths(nl)
for p in paths: p.add(moncalieri) # Moncalieri in coda a tutti i path
best = argmax(paths, key = p.getPrestigio())
Funzione obiettivo
Per un path p = [n₁, n₂, …, nₖ]:
prestigio(p) = prestigio(n₁)
+ Σ_{i=2..k} ( needBonus(n_{i-1}, n_i)
? MULTIPLIER · prestigio(n_i)
: prestigio(n_i) )
dove needBonus(a, b) ↔ a.type ≠ b.type e MULTIPLIER = 1.5.
Complessità
- Senza memoization:
O(n!)in tempo,O(n)in spazio di stack. - Con memoization su
Set<Node>: gli stati distinti sono2^n, ma per ciascuno serve costruireO(n)path → spazioO(n · 2^n), tempo dominato da costruzione e copia delle liste. Conn ≤ ~20resta tranquillamente gestibile. addAsFirstNodeèO(n)per via dell'aggiornamento del prestigio: il costo per path è quindiO(n²).
Note di correttezza
- La cache deve restituire copie difensive perché i chiamanti mutano i path tramite
addAsFirstNode/add. Vedi <ref_snippet file="/home/samuele/IdeaProjects/brusegan_samuele_VETF04000T_secondaprova/src/main/java/org/example/models/MainLogic.java" lines="51-58" /> e <ref_snippet file="/home/samuele/IdeaProjects/brusegan_samuele_VETF04000T_secondaprova/src/main/java/org/example/models/MainLogic.java" lines="76-79" />. - Il caso base
|nl| == LENGHT - K - 1taglia gli ultimiKlivelli di ricorsione restituendo path vuoti: occorre verificare che questo sia il comportamento voluto dalla traccia (probabilmente la traccia richiede esattamente un percorso lungoK+1nodi, partenza inclusa). Da chiarire/refinire una volta letto il PDF.
Struttura del progetto
.
├── pom.xml
├── nbactions.xml
├── src/
│ └── main/
│ ├── java/
│ │ ├── module-info.java
│ │ └── org/example/
│ │ ├── App.java # entry point JavaFX
│ │ ├── SystemInfo.java
│ │ ├── controller/
│ │ │ └── Controller.java # stub
│ │ ├── models/
│ │ │ ├── Importer.java # parser CSV
│ │ │ ├── Node.java
│ │ │ ├── Path.java
│ │ │ ├── PathBuilder.java # stub Runnable
│ │ │ └── MainLogic.java # ricorsione + cache + main CLI
│ │ └── views/
│ │ └── MainUI.java # stub UI
│ └── resources/
│ ├── punti_interesse.csv # dataset principale (14 POI)
│ └── punti_interesse_b.csv # dataset secondario (20 POI)
└── target/
Requisiti
- JDK 25 (vedi
<release>25</release>nelpom.xml) - Maven 3.9+ (incluso wrapper
.mvn/) - JavaFX 21.0.2 — risolto da Maven via
org.openjfx:javafx-controls
Build & Run
Compilazione
./mvnw clean compile
Esecuzione della logica (CLI)
Per ora il main di MainLogic stampa direttamente il miglior path su stdout:
./mvnw exec:java -Dexec.mainClass="org.example.models.MainLogic"
oppure, da IDE, eseguire la classe org.example.models.MainLogic.
Esecuzione dell'app JavaFX (stub)
./mvnw javafx:run
Apre una finestra placeholder.
Nota: l'
Importerusa un path relativo (./src/main/resources/punti_interesse.csv), quindi va lanciato dalla root del progetto.
Dataset
Due file CSV in src/main/resources/, stesso schema (senza header):
<nome>,<prestigio>,<tipo>
<nome>: stringa libera (no virgole)<prestigio>: numero (parsato comedouble)<tipo>:0(culturale) o1(panoramico)
L'ultima riga è sempre il punto di partenza (Castello di Moncalieri).
| File | Righe |
|---|---|
punti_interesse.csv |
14 |
punti_interesse_b.csv |
20 |