Red vožnje

Autor zadatka: Rade Radišić <radisic.rade@uns.ac.rs>

Napisati program koji na osnovu spiska polazaka sa autobuske stanice formira red vožnje. Iz ulazne datoteke učitati u binarno stablo pretrage sledeće podatke:

  • odredište (jedna reč, do 20 karaktera)

  • vreme_polaska (jedna reč, u formatu sat, dva karaktera, tačka, minut, dva karaktera)

  • prevoznik (jedna reč, do 30 karaktera)

  • cena_karte (realan broj)

Stablo se formira na osnovu vrednosti polja vreme_polaska.

Program prima ime ulazne, izlazne datoteke, odredište i vreme kao argumente komandne linije. U izlaznu datoteku potrebno je ispisati podatke iz ulazne datoteke, sortirane po vremenima polaska u rastućem redosledu. Prilikom ispisa u izlaznu datoteku, koristiti format "%9s %5s %-15s %7.2lf\n".

Na standardni izlaz ispisati najjeftiniji polazak za zadato mesto do naznačenog vremena polaska (uključujući i vreme polaska). Ukoliko postoji, ispisati:

Polazak pre <zadato-vreme> casova za mesto <zadato-mesto> sa najjeftinijom kartom je:

zajedno sa informacijama o polasku. Prilikom formatiranja jedino zaokružiti cenu karte na dve decimale. Ukoliko ne postoji, ispisati poruku sledeće sadržine:

Ne postoji polazak za mesto <zadato-mesto> pre <zadato-vreme> casova.

zadato-mesto i zadato-vreme predstavljaju vrednosti argumenata komandne linije.

Uraditi validaciju argumenta komandne linije koji predstavlja zadato vreme na sledeći način:

  • Iskontrolisati dužinu stringa tako da u njega stane format, po dve cifre za čas i za minut, sa tačkom između (proveriti i njeno postojanje)

  • Izdvojiti iz stringa čas i minut, pretvoriti u celobrojne vrednosti i iskontrolisati da li se nalaze u validnom opsegu

    • Čas je vrednost između 0 i 23

    • Minut je vrednost između 0 i 59

Radi jednostavnosti, pretpostaviti da se na mestima rezervisanim za časove i minute mogu naći samo cifre.

U slučaju sledećih grešaka prilikom rada programa, izaći sa odgovarajućim kodom greške:

  • Nedovoljan ili suvišan broj argumenata, kod greške 1 (EXIT_FAILURE)

  • Neuspešna validacija zadatog vremena, kod greške 2

  • Neuspešno otvaranje ulazne datoteke, kod greške 3

  • Neuspešno otvaranje izlazne datoteke, kod greške 4

  • Nije moguće dinamički zauzeti memoriju, kod greške 5

Program realizovati tako da sve funkcije koje imaju veze isključivo sa stablom (inicijalizacija, dodavanje, brisanje...) budu odvojene u jedan par datoteka koje predstavljaju zaglavlje i implementaciju.

Primer ulazne datoteke polasci.txt:

Beograd   08.55 Lasta            801.50
Beograd   10.00 Jugoprevoz       821.50
Beograd   11.35 Severtrans       821.50
Beograd   12.20 SaobracajZabalj  671.50
Becej     09.50 BecejPrevoz      632.50
Becej     12.10 BecejPrevoz      632.50
Becej     13.15 BecejPrevoz      632.50
Becej     13.45 Lasta            572.50
Subotica  08.25 Lasta            971.50
Subotica  10.35 NisEkspres      1211.50
Subotica  12.25 SaobracajZabalj  971.50
Subotica  13.50 NisEkspres      1381.50
Zrenjanin 08.58 BanatTrans       471.50
Zrenjanin 09.30 AsTours          461.50
Zrenjanin 10.30 StupVrsac        521.50
Zrenjanin 11.50 BanatTrans       471.50

Za primer poziva programa:

./a.out polasci.txt red-voznje.txt Zrenjanin 13.00

Očekivani ispis na standardnom izlazu je:

Polazak pre 13.00 casova za mesto Zrenjanin sa najjeftinijom kartom je: Zrenjanin 09.30 AsTours 461.50

Dok je za poziv sledeći poziv programa:

./a.out polasci.txt red-voznje.txt Zrenjanin 08.00

Očekivani ispis na standardnom izlazu:

Ne postoji polazak za mesto Zrenjanin pre 08.00 casova.

U oba slučaja, sadržaj izlazne datoteke red-voznje.txt je sledeći:

 Subotica 08.25 Lasta            971.50
  Beograd 08.55 Lasta            801.50
Zrenjanin 08.58 BanatTrans       471.50
Zrenjanin 09.30 AsTours          461.50
    Becej 09.50 BecejPrevoz      632.50
  Beograd 10.00 Jugoprevoz       821.50
Zrenjanin 10.30 StupVrsac        521.50
 Subotica 10.35 NisEkspres      1211.50
  Beograd 11.35 Severtrans       821.50
Zrenjanin 11.50 BanatTrans       471.50
    Becej 12.10 BecejPrevoz      632.50
  Beograd 12.20 SaobracajZabalj  671.50
 Subotica 12.25 SaobracajZabalj  971.50
    Becej 13.15 BecejPrevoz      632.50
    Becej 13.45 Lasta            572.50
 Subotica 13.50 NisEkspres      1381.50

Primer rešenja

  1#include <stdio.h>
  2#include <string.h>
  3#include <stdlib.h>
  4#include "stablo.h"
  5
  6POLAZAK *najjeftiniji_pre_vremena(POLAZAK *, char *, char *);
  7FILE *safe_fopen(char *, char *, int);
  8void ucitaj_polaske(FILE *, POLAZAK **);
  9void ispisi_polaske(FILE *, POLAZAK *);
 10
 11int validacija_vremena(char *);
 12
 13int main(int argc, char **argv) {
 14    POLAZAK *koren;
 15
 16    if(argc != 5) {
 17        printf("Primer poziva programa: %s polasci.txt red-voznje.txt Zrenjanin 13.00\n", argv[0]);
 18        exit(1);
 19    }
 20
 21    if(!validacija_vremena(argv[4])) {
 22        printf("Pogresan format vremena, primer dobrog formata: 13.00\n");
 23        exit(2);
 24    }
 25
 26    inicijalizacija(&koren);
 27
 28    FILE *ulazna = safe_fopen(argv[1], "r", 3);
 29    ucitaj_polaske(ulazna, &koren);
 30    fclose(ulazna);
 31
 32    FILE *izlazna = safe_fopen(argv[2], "w", 4);
 33    ispisi_polaske(izlazna, koren);
 34    fclose(izlazna);
 35
 36    POLAZAK *najjeftiniji = najjeftiniji_pre_vremena(koren, argv[3], argv[4]);
 37    if(najjeftiniji) {
 38        printf("Polazak pre %s casova za mesto %s sa najjeftinijom kartom je: %s %s %s %.2lf\n",
 39                argv[4], argv[3], najjeftiniji->odrediste, najjeftiniji->vreme_polaska, 
 40                najjeftiniji->prevoznik, najjeftiniji->cena_karte);
 41    } else {
 42        printf("Ne postoji polazak za mesto %s pre %s casova.\n", argv[3], argv[4]);
 43    }
 44
 45    return 0;
 46}
 47
 48POLAZAK *najjeftiniji_pre_vremena(POLAZAK *koren, char *odrediste, char *vreme) {
 49    POLAZAK *najjeftiniji = NULL;
 50
 51    if(koren != NULL) {
 52        if(strcmp(koren->odrediste, odrediste) == 0 &&
 53           strcmp(koren->vreme_polaska, vreme) <= 0) {
 54            najjeftiniji = koren; 
 55        }
 56
 57        POLAZAK *najjeftiniji_levi = najjeftiniji_pre_vremena(koren->levi, odrediste, vreme);
 58        if(najjeftiniji == NULL ||
 59           (najjeftiniji_levi != NULL && najjeftiniji_levi->cena_karte < najjeftiniji->cena_karte)) {
 60            najjeftiniji = najjeftiniji_levi;
 61        }
 62
 63        POLAZAK *najjeftiniji_desni = najjeftiniji_pre_vremena(koren->desni, odrediste, vreme);
 64        if(najjeftiniji == NULL ||
 65           (najjeftiniji_desni != NULL && najjeftiniji_desni->cena_karte < najjeftiniji->cena_karte)) {
 66            najjeftiniji = najjeftiniji_desni;
 67        }
 68    }
 69
 70    return najjeftiniji;
 71}
 72
 73FILE *safe_fopen(char *ime, char *rezim, int kod_greske) {
 74    FILE *fp = fopen(ime, rezim);
 75
 76    if(fp == NULL) {
 77        printf("Greska prilikom otvaranja %s datoteke!\n", ime);
 78        exit(kod_greske);
 79    }
 80
 81    return fp;
 82}
 83
 84void ucitaj_polaske(FILE *pulazna, POLAZAK **pkoren) {
 85    char tmp_odrediste[MAX_ODREDISTE];
 86    char tmp_vreme_polaska[MAX_VREME_POLASKA];
 87    char tmp_prevoznik[MAX_PREVOZNIK];
 88    double tmp_cena_karte;
 89
 90    while(fscanf(pulazna, "%s %s %s %lf",
 91                 tmp_odrediste,
 92                 tmp_vreme_polaska,
 93                 tmp_prevoznik,
 94                 &tmp_cena_karte) != EOF) {
 95        POLAZAK *novi = napravi_cvor(
 96            tmp_odrediste,
 97            tmp_vreme_polaska,
 98            tmp_prevoznik,
 99            tmp_cena_karte);
100        dodaj_u_stablo(pkoren, novi);
101    }
102
103}
104
105void ispisi_polaske(FILE *izlazna, POLAZAK *koren) {
106    if(koren != NULL) {
107        ispisi_polaske(izlazna, koren->levi);
108        fprintf(izlazna, "%9s %5s %-15s %7.2lf\n",
109                koren->odrediste,
110                koren->vreme_polaska,
111                koren->prevoznik,
112                koren->cena_karte);
113        ispisi_polaske(izlazna, koren->desni);
114    }
115}
116
117int validacija_vremena(char *vreme) {
118    int sat, minut;
119
120    if(strlen(vreme) != 5 || vreme[2] != '.') return 0;
121
122    sat = (vreme[0] - '0') * 10;
123    sat += vreme[1] - '0';
124
125    if(sat >= 24) return 0;
126
127    minut = (vreme[3] - '0') * 10;
128    minut += vreme[4] - '0';
129
130    if(minut >= 60) return 0;
131
132    return 1;
133}