Simulazione d`esame del 21/01/2016

Transcript

Simulazione d`esame del 21/01/2016
Algoritmi e Programmazione
Simulazione d’esame del 21/01/2016 - Prova di teoria (12 punti)
1. (2 punti)
Si disegni il BST risultante al termine dell’inserimento dei seguenti valori
40 9 21 25 35 91 8 70 16 14 12 20
Si esegua poi la cancellazione dei valori 91, 9 e 35, e si disegni nuovamente l’albero risultante.
2. (2 punti)
Si consideri una tabella di hash basata su open addressing che utilizza la tecnica del linear probing per risolvere
le collisioni. La tabella è inizialmente vuota. Su di essa viene eseguita una sequenza di operazioni di inserimento di
elementi con chiave corrispondente alle seguenti stringhe:
orazio, pippo, pluto, paperino, minnie, gastone, qui,
quo, qua, paperoga, topolino, paperina,
Si mostri il contenuto della tabella di hash al termine della sequenza di inserimenti, assumendo che la sua
dimensione sia pari a M=13 e che la funzione di hash utilizzata corrisponda al resto della divisione tra la
somma dei codici ASCII delle lettere componenti la stringa ed il valore M.
Si ricorda che il codice ASCII della lettera ‘a’ è 97 e della lettera ‘z’ è 122.
3. (2 punti)
Sia data una coda a priorità inizialmente vuota implementata mediante uno heap. Sia data la sequenza di interi e
carattere *:
20 32 19 51 * * 28 * 74 9 81 * * 17 * 41 33 18
dove ad ogni intero corrisponde un inserimento nella coda a priorità e al carattere * un’estrazione con
cancellazione del massimo. Si riporti la configurazione della coda a priorità dopo ogni operazione e la sequenza dei
valori restituiti dalle estrazioni con cancellazione del massimo.
Al termine si cambi la priorità di 41 in 11 e si disegni la configurazione risultante della coda a priorità.
4. (1 punto)
Si esprima in notazione prefissa e postfissa la seguente espressione aritmetica mediante visita dell’albero binario
corrispondente: ((A +B) * (C*(D-E))) /((E-F) + G*H)
5. (5 punti)
Sia dato il seguente grafo orientato e pesato:
– Si disegnino la matrice di adiacenze e la lista di adiacenze del grafo.
– Si determinino i valori di tutti i cammini minimi che collegano il vertice A con ogni altro vertice
mediante l’algoritmo di Dijkstra. Si assuma, qualora necessario, un ordine alfabetico per i vertici e
gli archi. Si riportino i passaggi dell'algoritmo in modo da evincere l'ordine di analisi dei nodi e le
operazioni di aggiornamento dei cammini minimi.
Algoritmi e Programmazione
Simulazione d’esame del 21/01/2016 - Prova di programmazione (18 punti)
Una rete stradale collega un certo numero di città fra loro.
Ogni tratto di strada inizia e termina in una città, e può essere percorso in entrambi i sensi.
I vari tratti possono appartenere alla rete autostradale, quindi a pedaggio, o alla rete pubblica gratuita. Ogni tratto
inoltre ha una propria lunghezza.
La rete stradale è descritta da un file nel seguente formato:
<numero città>
<città_a> <città_b> <lunghezza> <pedaggio>
Dopo il caricamento il programma deve visualizzare alcune statistiche sul grafo:
• numero di tratti autostradali;
• numero di tratti pubblici;
• massimo pedaggio di un singolo tratto.
Date due città esiste (almeno) un cammino minimo tra esse.
Scrivere un programma che chieda all'utente quali sono le città di partenza e arrivo e calcoli il percorso di lunghezza
minima tra esse.
Se esistono più percorsi minimi sceglierne uno qualunque.
Visualizzare il percorso calcolato stampando i nomi di tutte le città attraversate, comprese la partenza e l'arrivo, e la
sua lunghezza.
Un'automobile, d'altra parte, ha un proprio consumo chilometrico di carburante. Per semplicità si consideri costante
tale consumo.
Ad ogni tratto di strada, perciò, corrisponde un costo pari alla somma di pedaggio e costo del carburante.
Si completi il programma facendo in modo che, chiesti all'utente il consumo chilometrico dell'automobile e il prezzo
attuale del carburante, calcoli il percorso di costo minimo tra le due città precedentemente scelte.
Visualizzare quindi entrambi i percorsi calcolati, la loro lunghezza ed il loro costo.
Si trascuri la gestione degli errori.
Algoritmi e Programmazione
Soluzione della Simulazione d’esame del 21/01/2016 - Prova di teoria (12 punti)
1. (2 punti)
Si disegni il BST risultante al termine dell’inserimento dei seguenti valori
40 9 21 25 35 91 8 70 16 14 12 20
Si esegua poi la cancellazione dei valori 91, 9 e 35, e si disegni nuovamente l’albero risultante.
2. (2 punti)
Si consideri una tabella di hash basata su open addressing che utilizza la tecnica del linear probing per risolvere
le collisioni. La tabella è inizialmente vuota. Su di essa viene eseguita una sequenza di operazioni di inserimento di
elementi con chiave corrispondente alle seguenti stringhe:
orazio, pippo, pluto, paperino, minnie, gastone, qui,
quo, qua, paperoga, topolino, paperina,
Si mostri il contenuto della tabella di hash al termine della sequenza di inserimenti, assumendo che la sua
dimensione sia pari a M=13 e che la funzione di hash utilizzata corrisponda al resto della divisione tra la
somma dei codici ASCII delle lettere componenti la stringa ed il valore M.
Si ricorda che il codice ASCII della lettera ‘a’ è 97 e della lettera ‘z’ è 122.
97
a
98
b
99
c
100
d
101
e
102
f
103
g
104
h
105
i
106
j
107
k
108
l
109
m
110
n
111
o
112
p
113
q
114
r
115
s
116
t
117
u
118
v
119
w
120
x
121
y
122
z
Ks
Qui = 336
Quo = 342
Qua = 328
Paperoga = 847
Topolino = 884
Paperina = 848
Orazio = 111+114+97+122+105+111= 660
Pippo = 562
Pluto = 564
Paperino = 863
Minnie = 640
Gastone = 753
H(k) = k mod M
M= 13
Hs
H(660)
10
H(562)
3
Topolino -
H(564)
5
H(863)
5,6
Paperoga Pippo
H(640)
3,4
H(753) H(336)
12
11
Minnie Pluto
H(342)
4,5,6,7
Paperino Quo
Qua
H(328) H(847)
3,…,8 2
H(884)
0
Paperina Orazio Qui
H(848)
3,…,9
Gastone
3. (2 punti)
Sia data una coda a priorità inizialmente vuota implementata mediante uno heap. Sia data la sequenza di interi e
carattere *:
20 32 19 51 * * 28 * 74 9 81 * * 17 * 41 33 18
dove ad ogni intero corrisponde un inserimento nella coda a priorità e al carattere * un’estrazione con
cancellazione del massimo. Si riporti la configurazione della coda a priorità dopo ogni operazione e la sequenza dei
valori restituiti dalle estrazioni con cancellazione del massimo.
Al termine si cambi la priorità di 41 in 11 e si disegni la configurazione risultante della coda a priorità.
Valori Estratti
51
32
28
81
74
20
4. (1 punto)
Si esprima in notazione prefissa e postfissa la seguente espressione aritmetica mediante visita dell’albero binario
corrispondente: ((A +B) * (C*(D-E))) /((E-F) + G*H)
Espressione in notazione Postfissa: ab+cde-**ef-gh*+/ (Ottenuta con Visita Post-Order)
Espressione in notazione Prefissa: /(*(+(a,b),*(c,-(d,e))),+(-(e,f),*(g,h))) (Ottenuta con Visita Pre-Order)
5. (5 punti)
Sia dato il seguente grafo orientato e pesato:
– Si disegnino la matrice di adiacenze e la lista di adiacenze del grafo.
– Si determinino i valori di tutti i cammini minimi che collegano il vertice A con ogni altro vertice
mediante l’algoritmo di Dijkstra. Si assuma, qualora necessario, un ordine alfabetico per i vertici e
gli archi. Si riportino i passaggi dell'algoritmo in modo da evincere l'ordine di analisi dei nodi e le
operazioni di aggiornamento dei cammini minimi.
Matrice di Adiacenza
A
0
0
0
0
0
0
0
A
B
C
G
H
K
L
B
1
0
0
0
0
0
0
C
0
3
0
0
0
0
0
G
4
0
0
0
0
0
1
Lista delle Adiacenze
A
B
C
G
H
K
L
B
1
G
4
C
3
H
2
k
G
L
2
k
1
K
3
3
H
5
H
0
2
0
0
0
0
5
K
0
0
0
1
3
0
3
L
2
0
0
0
0
0
0
Dijkstra
Algoritmi e Programmazione
Simulazione d’esame del 21/01/2016 - Prova di programmazione (18 punti)
Una rete stradale collega un certo numero di città fra loro.
Ogni tratto di strada inizia e termina in una città, e può essere percorso in entrambi i sensi.
I vari tratti possono appartenere alla rete autostradale, quindi a pedaggio, o alla rete pubblica gratuita. Ogni tratto
inoltre ha una propria lunghezza.
La rete stradale è descritta da un file nel seguente formato:
<numero città>
<città_a> <città_b> <lunghezza> <pedaggio>
Dopo il caricamento il programma deve visualizzare alcune statistiche sul grafo:
• numero di tratti autostradali;
• numero di tratti pubblici;
• massimo pedaggio di un singolo tratto.
Date due città esiste (almeno) un cammino minimo tra esse.
Scrivere un programma che chieda all'utente quali sono le città di partenza e arrivo e calcoli il percorso di lunghezza
minima tra esse.
Se esistono più percorsi minimi sceglierne uno qualunque.
Visualizzare il percorso calcolato stampando i nomi di tutte le città attraversate, comprese la partenza e l'arrivo, e la
sua lunghezza.
Un'automobile, d'altra parte, ha un proprio consumo chilometrico di carburante. Per semplicità si consideri costante
tale consumo.
Ad ogni tratto di strada, perciò, corrisponde un costo pari alla somma di pedaggio e costo del carburante.
Si completi il programma facendo in modo che, chiesti all'utente il consumo chilometrico dell'automobile e il prezzo
attuale del carburante, calcoli il percorso di costo minimo tra le due città precedentemente scelte.
Visualizzare quindi entrambi i percorsi calcolati, la loro lunghezza ed il loro costo.
Si trascuri la gestione degli errori.
Svolgimento proposto da uno Studente.
Il testo parla di città collegate da strade percorribili in entrambi i sensi. Questo significa che la struttura dati
richiesta per memorizzare le informazioni è un grafo pesato non orientato. Non viene detto nulla sulle città,
mentre si specifica che le strade hanno una lunghezza ed un possibile pedaggio. Ciò vuol dire che bisogna
memorizzare entrambe le informazioni. Visto che non si ricorda a memoria d'uomo una strada per percorrere la quale
si venga pagati si può anche supporre che le strade senza pedaggio abbiano semplicemente un prezzo zero.
Il grafo può essere memorizzato sia usando la matrice che le liste di adiacenza. Entrambe però vanno modificate in
modo da contenere le informazioni sugli archi: il modo più semplice è sostituire l'intero 0/1 che indica incidenza con
una struttura che contenga sia la lunghezza che il pedaggio.
Il numero di città è noto appena iniziato a leggere il file, quindi sia la matrice che il vettore di puntatori che
“sostiene” le liste possono essere dimensionati per intero.
Ogni città però nel file è identificata da un nome, non dal corrispondente indice all'interno delle strutture dati.
Per trasformare un nome in un indice bisogna usare un contenitore associativo. I principali contenitori associativi
conosciuti sono la lista, l'albero binario e la tabella di hash.
Poiché la struttura deve solo essere riempita (non si effettuano rimozioni se non per liberare la memoria) e il
numero di elementi è noto prima che la struttura stessa debba essere usata, il contenitore più adatto per la
situazione è certamente la tabella di hash poiché garantisce le prestazioni migliori a fronte di un'implementazione
piuttosto semplice.
L'albero binario costituisce una alternativa accettabile, anche se meno rapida nell'esecuzione.
In ogni caso il contenitore dovrà contenere sia il nome delle città che i corrispondenti indici nelle strutture dati.
L'ordine più comodo nel quale memorizzare i vertici del grafo è quello in cui compaiono nel file: la prima volta che
si trova un nome si assegna una posizione a quella città e si inserisce la coppia nome-posizione nel contenitore.
A fronte di una coppia di città si inserisce l'arco corrispondente nel grafo.
Poiché il grafo non è orientato si inserisce in modo simmetrico tra le due città, ossia sia dalla prima verso la seconda
che viceversa.
A questo punto risulta banale raccogliere le statistiche richieste: a parte il fatto che si possono calcolare a mano a
mano che si legge il file, è possibile anche scandire tutti gli archi del grafo e memorizzare il numero di tratti
autostradali e il massimo pedaggio.
Bisogna ricordarsi che, essendo il grafo non orientato, ogni tratto di strada vi è descritto due volte, per l'andata e il
ritorno.
Viene successivamente chiesto di calcolare un cammino minimo tra due città.
Gli strumenti a disposizione sono due algoritmi (Dijkstra e Bellman-Ford) che calcolano tutti i cammini minimi a
partire da un vertice. Questi strumenti sono sovrabbondanti rispetto alle esigenze, ma adatti allo scopo.
La differenza principale tra i due è l'efficienza ottenibile: l'algoritmo di Bellman-Ford è decisamente più lento, anche
se più semplice da implementare.
Entrambi gli algoritmi forniscono il cammino minimo tra la partenza e l'arrivo sotto forma di un vettore di
predecessori, uno per ogni vertice del grafo tranne quello di partenza.
È quindi molto facile visualizzare il cammino dalla destinazione alla partenza, meno il viceversa.
Per ottenere un elenco in ordine delle città attraversate si può procedere in due modi: uno è calcolare i cammini dalla
partenza e invertire in seguito la rappresentazione dalla destinazione alla partenza; un altro è calcolare i cammini
minimi a partire dalla destinazione e visualizzare la rappresentazione ottenuta. Questo è possibile solo perché il
grafo è non orientato.
L'unica difficoltà nel realizzare l'algoritmo di Dijkstra è la coda a priorità necessaria per effettuare i rilassamenti
nell'ordine corretto.
Si può realizzare utilizzando uno heap analogo a quello impiegato nello heapsort, ma con una proprietà opposta:
invece di imporre che il contenuto di ogni nodo sia maggiore di quello di entrambi i figli ci si assicura che sia
minore.
Bisogna ricordarsi di conservare, per ogni elemento della coda, non solo la stima della distanza ma anche un
riferimento a quale sia il nodo relativo. Anzi, la soluzione migliore è conservare in coda solo i riferimenti ai vertici,
ed usare le stime di distanza di ogni vertice come chiave per la gestione dello heap; in questo modo si evita di dover
mantenere coerenti insiemi diversi di dati.
È utile anche mantenere una informazione inversa, cioè la posizione nella coda di ogni vertice.
Ad ogni rilassamento conviene ripristinare la proprietà dello heap: il nodo modificato nello heap dovrà risalire verso
la radice fino a quando il padre ha un contenuto maggiore.
Sia per l'estrazione dallo heap che per il ripristino del medesimo dopo ogni rilassamento occorre ricordarsi di
aggiornare l'indicazione della posizione in coda dei vertici toccati.
L'uso dell'algoritmo di Bellman-Ford elimina tutti questi problemi, ma porta ad una complessità decisamente
maggiore.
Il terzo punto chiede semplicemente una riapplicazione dello stesso algoritmo usando un nuovo peso per gli archi. La
soluzione più semplice è aggiungere alla struttura che contiene le informazioni sugli archi un terzo campo contenente
il costo totale.
In alternativa si può riciclare il campo pedaggio, contando sul fatto di non dover più calcolare altri costi. La seconda
soluzione è di realizzazione più rapida, ma meno facile da modificare.
File main.c
/* Created by Anjuta version 1.2.4a */
/* This file will not be overwritten */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#define BUFFLEN 100
typedef struct Strada {
int citta;
int lunghezza;
int pedaggio;
int costo_tot;
struct Strada *next;
} Strada;
typedef struct Citta {
int citta_precedente;
int distanza;
int pos_heap;
int visitato;
struct Strada *strade;
} Citta;
//funzione di debug per la stampa del grafo dei collegamenti
void stampa_grafo(char **tabella_citta,Citta *mappa,int dim_tabella) {
int i;
Strada **str_attuale;
for(i=0;i<dim_tabella,i++) {
if (tabella_citta[i]!=NULL) {
printf("%s",tabella_citta[i]);
str_attuale=&(mappa[i].strade);
while (*str_attuale!=NULL) {
printf("-->%s",tabella_citta[(*str_attuale)->citta]);
str_attuale=&(*str_attuale)->next);
}
putchar('\n');
} else printf("NULL\n");
};
putchar('\n');
}
//crea la tabella di hash delle citta di dimensione n_citta+n_citta/2
char **crea_hash_citta(int n_citta,int *dim_tabella) {
char **tabella_citta;
int i;
*dim_tabella=n_citta+n_citta/2;
tabella_citta=(char **)malloc(*dim_tabella*sizeof(char *));
for(i=0;i<*dim_tabella;i++) {
tabella_citta[i]=NULL;
}
return tabella_citta;
}
//cerca una citta nella tabella,se la trova mi restituisce 1 altrimenti 0 e mette in ind_trovato
//la posizione della città o della casella vuota in cui metterla
int cerca_citta_hash(char **tabella_citta,int dim_tabella,char *nome_citta ,int *ind) {
unsigned long int h=5381ul;
int i;
/*
// << -> operatore per lo shift a sinistra
int a=10;
// rappresentazione binaria (8 bit): 00001010
int b=2;
int c;
c = a<<b;
// c = 00101000 = 40
*/
for(i=0;i<strlen(nome_citta);i++) h=((h<<5)+h)+nome_citta[i];
*ind=h%dim_tabella;
i = 0;
while ((tabella_citta[*ind]!=NULL)&&(strcmp(tabella_citta[*ind],nome_citta)!=0)) {
*ind=(h+i*i)%dim_tabella;
i++;
};
if (tabella_citta[*ind]==NULL) return 0;
else return 1;
}
//aggiunge la città nella [ind] della tabella
void add_citta_hash(char **tabella_citta,char *nome_citta,int ind ) {
int lungh_citta;
lungh_citta=strlen(nome_citta);
tabella_citta[ind]=(char *)malloc(lungh_citta+1);
strcpy(tabella_citta[ind],nome_citta);
}
Citta *crea_mappa(int dim_tabella) {
int i;
Citta *mappa;
mappa=(Citta *)malloc(dim_tabella*sizeof(Citta));
for(i=0;i<dim_tabella;i++) {
mappa[i].citta_precedente=-1;
mappa[i].distanza=-1;
mappa[i].pos_heap=-1;
mappa[i].visitato=0;
mappa[i].strade=NULL;
}
void add_strada(Citta *mappa; int part, int dest, int lingh, int pedaggio) {
Strada **str_attuale;
str_attuale=&(mappa[part].strade);
while (*str_attuale!=NULL) str_attuale=&((*str_attuale)->next);
*str_attuale=(Strada *)malloc(sizeof(Strada));
(*str_attuale)->citta=dest;
(*str_attuale)->lunghezza=lungh;
(*str_attuale)->pedaggio=pedaggio;
(*str_attuale)->next=NULL;
}
/*----Gestine con heap non finito e non implementato---int *crea_heap(int n_citta) {
int *heap;
heap=(int *)malloc(n_citta*sizeof(int));
return heap;
}
void heap_init(int *heap,int n_citta) {
int i;
for(i=0;i<n_citta;i++) heap[i]=-1;
}
//riorganizza lo heap dopo che l'elemento 'ind' ha cambiato distanza
//(n_elem è il numero di elementi nello heap)
int *heap_riorganizza(int *heap,Citta *mappa,int n_elem,int ind) {
int temp_pos_heap, temp_elem, child;
temp_pos_heap=mappa[ind].pos_heap; //salvo la posizione di 'ind' nello heap
temp_elem=ind;
//faccio risalire l'elemento
// da implementare
}
//inserisce l'elemento 'ind' nello heap assegnandogli peso 'distanza'
int *heap_add(int *heap,Citta *mappa,int *n_elem,int ind, int distanza) {
heap[*n_elem]=ind;
mappa[ind].pos_heap=*n_elem;
mappa[ind].distanza=distanza;
(*n_elem)++;7heap_riorganizza(heap,mappa,*n_elem,(*n_elem)-1);
}
int heap_extract(int *heap,Citta *mappa,int *n_elem) {
int primo_elemento;
primo_elemento=heap[0];
if(primo_elemento!=-1) {
mappa[primo_elemento].pos_heap=-1;
if(*n_elem>1) {
heap[0]=heap[*n_elem-1];
heap[*n_elem-1]=-1;
mappa[*n_elem-1].pos_heap=0;
};
*n_elem--;
};
heap_riorganizza(heap,mappa,*n_elem,0);
return primo_elemento;
}
//cambia la distanza dell'elemento 'ind' con 'new_dist'
int *heap_aggiorna_distanza(int *heap,Citta *mappa,int n_elem,int ind,int new_dist) {
mappa[ind].distanza=new_dist;
heap_riorganizza(heap,mappa,n_elem,ind);
}
--------------------------------------------*/
int percorso_minimo(Citta *mappa,int n_citta,int dim_tabella,int partenza, int arrivo) {
int i,attuale,new_dist;
Strada *strada;
for(i=0;i<dim_tabella;i++) {
mappa[i].distanza=INT_MAX;
mappa[i].citta_precedente=-1;
mappa[i].visitato=0;
};
mappa[partenza].distanza=0; /* enqueue(partenza,0) */
while (attuale!=arrivo) {
//cerco la prima città raggiunta non ancora visitata
attuale = 0;
while (mappa[attuale].visitato!=0) attuale++;
//cerco la città a distanza minore tra le città raggiunte
for(i=attuale+1;i<dim_tabella;i++) {
if((mappa[i].visitato==0)&&(mappa[attuale].distanza>mappa[i].distanza)) attuale=i;
};
/*dequeue()*/
mappa[attuale].visitato=1;
strada=mappa[attuale].strade;
while(strada!=NULL) {
new_dist=mappa[attuale].distanza+strada->costo_tot;
/*
if(isInQueue(strada->citta)) {
if(mappa[strada->citta].distanza>new_dist) {
chagePriority(attuale->citta,newdist);
mappa[strada->citta].citta_precedente=attuale;
};
else enqueue(strada->citta,strada->costo_tot);
};
*/
if(mappa[strada->citta].distanza>new_dist) {
mappa[strada->citta].distanza=new_dist;
mappa[strada->citta].citta_precedente=attuale;
};
strada=strada->next;
};
};
return mappa[attuale].distanza;
}
void stampa_cammino_minimo(Citta *mappa,char **tabella_citta,int partenza) {
while(mappa[partenza].citta_precedente!=-1) {
printf("%s ",tabella_citta[partenza]);
partenza=mappa[partenza].citta_precedente;
};
printf("%s \n",tabella_citta[partenza]);
//stampa l'ultima citta
}
void inizializza_distanza(Citta *mappa,int dim_tabella) {
Strada *attuale;
int i;
for(i=0;i<dim_tabella;i++) {
attuale = mappa[i].strade;
while(attuale!=NULL) {
attuale->costo_tot=attuale->lunghezza;
attuale=attuale->next;
};
};
}
void inizializza_costo_totale(Citta *mappa,int dim_tabella,int consumo) {
Strada *attuale;
int i;
for(i=0;i<dim_tabella;i++) {
attuale=mappa[i].strade;
while(attuale!=NULL) {
attuale->costo_tot=(attuale->lunghezza)*consumo+(attuale->pedaggio);
attuale=attuale->next;
};
};
}
void free_mappa(Citta *mappa,int dim_tabella) {
Strada *attuale;
int i;
for(i=0;i<dim_tabella;i++) {
attuale=mappa[i].strade;
while(attuale!=NULL){
mappa[i].strade=attuale->next;
free(attuale);
attuale=mappa[i].strade;
};
};
free(mappa);
}
int main() {
FILE *in;
char buffer1[BUFFLEN],buffer2[BUFFLEN],line[BUFFLEN];
int n_citta,lunghezza,pedaggio,dim_tabella,n_autostrade=0,n_pubblici =0,max_pedaggio=0;
int ind1,ind2,citta_trovata,consumo;
char **tabella_citta;
Citta *mappa;
in=fopen("map.txt","r");
fgets(line,BUFFLEN,in);
sscanf(line,"%d",&n_citta);
tabella_citta=crea_hash_citta(n_citta,&dim_tabella);
mappa=crea_mappa(dim_tabella);
fgets(line,BUFFLEN,in);
while(!feof(in)) {
sscanf(line,"%s %s %d %d",buffer1,buffer2,&lunghezza,&pedaggio);
if (pedaggio==0) n_pubblici++;
else n_autostrade++;
if (pedaggio>max_pedaggio) max_pedaggio=pedaggio;
citta_trovata=cerca_citta_hash(tabella_citta,dim_tabella,buffer1,& ind1);
if(citta_trovata==0) {
add_citta_hash(tabella_citta,buffer1,ind1);
};
citta_trovata=cerca_citta_hash(tabella_citta,dim_tabella,buffer2,& ind2);
if(citta_trovata==0) {
add_citta_hash(tabella_citta,buffer2,ind2);
};
add_strada(mappa,ind1,ind2,lunghezza,pedaggio);
add_strada(mappa,ind2,ind1,lunghezza,pedaggio);
fgets(line,BUFFLEN,in);
};
stampa_grafo(tabella_citta,mappa,dim_tabella);
printf("Numero di tratti autostradali: %d\n",n_autostrade);
printf("Numero tratti pubblici: %d\n",n_pubblici);
printf("Massimo pedaggio: %d\n\n",max_pedaggio);
printf("inserisci citta di partenza --> ");
scanf("%s",buffer1);
printf("inserisci citta di arrivo --> ");
scanf("%s",buffer2);
//heap=crea_heap(n_citta);
cerca_citta_hash(tabella_citta,dim_tabella,buffer1,&ind1);
cerca_citta_hash(tabella_citta,dim_tabella,buffer2,&ind2);
inizializza_distanza(mappa,dim_tabella);
lunghezza=percorso_minimo(mappa,n_citta,dim_tabella,ind2,ind1);
printf("\nIl percorso di lunghezza minima è:\n");
stampa_cammino_minimo(mappa,tabella_citta,ind1);
printf("la sua lunghezza è: %d\n",lunghezza);
printf("inserisci consumo chilometrico --> ");
scanf("%d",&consumo);
inizializza_costo_totale(mappa,dim_tabella,consumo);
lunghezza=percorso_minimo(mappa,n_citta,dim_tabella,ind2,ind1);
printf("\nIl percorso di costo minimo è:\n");
stampa_cammino_minimo(mappa,tabella_citta,ind1);
printf("il suo costo totale è: %d\n",lunghezza);
free_mappa(mappa,dim_tabella);
free(tabella_citta);
return (0);
}
Commenti alla soluzione proposta dallo studente
Con la soluzione proposta lo studente è riuscito a superare l’esame mostrando di conoscere le strutture dati e le
tecniche di programmazione apprese durante il corso di APA.
Sono stati opportunamente utilizzati l’Heap, la Tabella di Hash, e sono state implementate le Visite ai Grafi, oltre
aver dimostrato capacità di analisi del problema e progettazione del Software.
Nonostante il buon risultato ci sono alcuni particolari che permettono di migliorare il codice proposto.
Innanzitutto separare Interfaccia ed Implementazione delle Funzioni, scriviamo le prime prima del main e poi le
sviluppiamo dopo.
Meglio ancora includere in librerie le nostre funzioni che poi andremo ad allegare all’esame risparmiando
notevole tempo che sarà così possibile dedicare alla progettazione, revisione ed ottimizzazione del software prodotto.
#include “my_heap.h”;
#include “my_hash_tables.h”;
#include “my_graphs.h”;
Si ricorda di lavorare sempre con approccio top-dow, partire dal main scomponendo il problema nei vari
sottoproblemi che gestiremo con le funzioni che svilupperemo in un secondo momento.
Altre considerazioni sarebbero semplicemente scelte di implementazione che restano totalmente soggettive, ad
esempio avrei creato 2 Liste di Adiacenze, una alla lettura del file per le distanze ed una in base al consumo dell’auto
per i costi facenti riferimento al nome del nodo (città) invece di tenere unite tutte le informazioni in un heap
complicandone la gestione: fondamentalmente sono due cammini minimi con pesi differenti gestibili con una sola
funzione di Djikstra a cui verranno passati i nodi di partenza ed arrivo e la lista di adiacenza rappresentante la realtà
in valutazione.