Minta nagy ZH

Czirkos Zoltán · 2017.07.13.

Minta nagy ZH, előző évek feladataiból összeollózva.

Minta nagy ZH, néhány előző évekből származó feladatból összeállítva. Tudnivalók a feladatsor felhasználásával kapcsolatban: lásd itt. Ez csak minta. Nem minden évben ugyanakkor volt az első NZH, nem minden évben pont ugyanaddig terjedt az első NZH anyaga. A pontozás is minta, tájékoztató jellegű.

1. Első NZH – minta

Ötöslottó

BEUGRÓ: Írj főprogramot, amelyik létrehoz egy tömböt öt lottószám számára, és a lotto() nevű függvényt meghívja rá! A függvény dolga lesz feltölteni a tömböt. Írd ki ezután a számokat a képernyőre!

Írd meg a lotto() függvényt, amelyik az ötöslottó sorsolását szimulálja, vagyis a paraméterként kapott tömbbe tesz öt, egymástól különböző, 1 és 90 közötti, véletlenszerűen választott egész számot! (A program próbálkozhat többször is a számsor előállításával.)

Egészítsd ki ezeket teljes programmá! Biztosítsd, hogy ne mindig ugyanazt az öt számot sorsolja a program!

Megoldás

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>

void lotto(int *szamok) {
    int i, j;
    for (i = 0; i < 5; ++i) {
        bool sorsol = true;
        while (sorsol) {
            szamok[i] = rand()%90 + 1;
            sorsol = false;
            for (j = 0; j < i; j++)
                if (szamok[j] == szamok[i])
                    sorsol = true;
        }
    }
}

int main(void) {
    srand(time(NULL));

    int szamok[5];
    int j;
    lotto(szamok);
    for (j = 0; j < 5; j++)
        printf("%d ", szamok[j]);

    return 0;
}

Pontozás, beugró értékelése:

  • BEUGRÓ: a főprogramban tömb létrehozása, függvény hívása, és a tömböt bejáró ciklus helyessége.
  • 1 p, fejlécfájlok, a 4-ből legalább 3 legyen meg.
  • 5 p lotto(), ebből 1 p fejléc, 1 p újra próbálkozás, 1 p véletlenszám generálás, 2 p keresés.
  • 4 p main(), ebből 1 p tömb, 1 p fv hívása, 1 p kiírás, 1 p srand (csak a főprogramban).

Időpontok

Definiálj típust, amelyben egy időpontot tudsz tárolni, külön óra (0...23), perc (0...59) és másodperc (0...59) értékekkel!

Írj függvényt, amelyben paraméterként egy sztringet veszel át, benne egy időponttal! Értelmezd a sztringet, majd add vissza az időpontot az előbb definiált típussal! A sztringben az időpont az alábbi három forma egyikében lesz:

  • 23:17:06 – óra, perc, másodperc, mindegyik két számjeggyel;
  • 15h 09m 53s – itt is óra, perc, másodperc, mindegyik két számjeggyel;
  • 10:15 AM – itt az óra és a perc két-két számjeggyel, a másodperc pedig nincs megadva, 0-nak kell tekinteni. Ez a formátum 12 órás; 12:00 AM = éjfél, 08:00 AM = reggel 8, 12:00 PM = dél, 05:00 PM = a délután 5 órai tea időpontja, azaz 17 óra.

Írj főprogramot, amelyben létrehozol három időpontot tároló változót, és a megírt függvényt használva feltöltöd őket a fenti példaidőpontokkal!

Megoldás

Alább két megoldás is látható, de természetesen elég volt egy megoldást adni. Az első a sscanf() függvényen alapul; az jelzi, hogy hány számot sikerült beolvasni. Ha a formátum stimmel, akkor ez 3 kell legyen. A második megoldás nyersebb, közvetlenül a karakterekkel dolgozik. A lényege ennek is az, hogy valahogyan fel kell ismerni, melyik formátumról van szó. Ha a sztring ötödik karaktere egy kettőspont, akkor csak az első lehet; ha a sztring második karaktere egy h betű, akkor pedig a második.

#include <stdio.h>

typedef struct Idopont {
    int ora, perc, masodperc;
} Idopont;

Idopont megoldas1(char *szoveg) {
    Idopont i;
    char ampm;
    if (sscanf(szoveg, "%d:%d:%d", &i.ora, &i.perc, &i.masodperc) == 3)
        return i;
    if (sscanf(szoveg, "%dh %dm %ds", &i.ora, &i.perc, &i.masodperc) == 3)
        return i;
    sscanf(szoveg, "%d:%d %cM", &i.ora, &i.perc, &ampm);
    i.masodperc = 0;
    i.ora %= 12;
    if (ampm == 'P')
        i.ora += 12;
    return i;
}

Idopont megoldas2_seged(char *ora, char *perc, char *mperc) {
    Idopont i;
    i.ora = (ora[0]-'0') * 10 + (ora[1]-'0');             /* vagy stdlib.h atoi() */
    i.perc = (perc[0]-'0') * 10 + (perc[1]-'0');
    i.masodperc = (mperc[0]-'0') * 10 + (mperc[1]-'0');
    return i;
}

Idopont megoldas2(char *szoveg) {
    Idopont i;
    if (szoveg[5] == ':')
        return megoldas2_seged(szoveg+0, szoveg+3, szoveg+6);
    if (szoveg[2] == 'h')
        return megoldas2_seged(szoveg+0, szoveg+4, szoveg+8);
    i = megoldas2_seged(szoveg, szoveg+3, "00");
    i.ora %= 12;
    if (szoveg[6] == 'P')
        i.ora += 12;
    return i;
}

int main() {
    Idopont a, b, c;
    a = megoldas2("23:17:06");
    b = megoldas2("15h 09m 53s");
    c = megoldas2("10:15 AM");
    return 0;
}

A pontozás, az alábbi részfeladatok helyes megoldására:

  • 1 p, struktúra definíciója, struktúra típusú változó létrehozása
  • 1 p, char 'x' és sztring "x", aposztróf/idézőjel helyes használata
  • 2 p, sztring átadása függvénynek, sztringek helyes kezelése (indexelés stb.)
  • 2 p, algoritmus: időpont formátumok megkülönböztetése
  • 3 p, számok kiszedése a sztringekből (bármilyen módszerrel); am/pm kezelése
  • 1 p, főprogram, változók létrehozása, saját függvény helyes használata

Súlyos hibák:

  • Sztringek helytelen kezelése
  • Lezáró nulla fogalmának nem ismerete, sztring méretének átadása
  • Túlindexelés
  • Kódduplikáció

Továbbá az általános pontozási irányelvek.

Szállóvendégek

Egy hétemeletes szállodában a szobafoglalásokat tömbben tárolják. A szobák a szokásos módon vannak számozva, a százasok adják meg az emeletet, a többi pedig a szoba sorszámát (pl. 712 = 7. emelet, 12. szoba). A földszint a 0. szint, utána 1-től 7-ig az emeletek. Ennél a feladatnál nem kell teljes programot írni, csak a megadott részeket.
  • BEUGRÓ: Definiálj Vendeg nevű típust, amelyik egy szállóvendég adatait (név: max. 50 karakter, szobaszám: egész) tartalmazza! Írj függvényt, amely átvesz egy vendéget, és visszaadja, hogy melyik emeleten lakik!
  • Írj függvényt, amely átvesz egy Vendeg elemekből álló tömböt és egy nevet! Keresse ez meg a névhez tartozó foglalást és adja vissza a megtalált tömbelem címét vagy NULL-t, ha nincs találat!
  • Írj függvényt, amely paraméterként kapja a vendégek tömbjét és egy másik, inicializálatlan tömböt, amelyet a szint sorszámával indexelünk! Írja be az utóbbi tömbbe, hogy az egyes emeleteken hány vendég lakik! Írj függvényt, amely megkapja a vendégek tömbjét, az előző függvénnyel előállítja a betöltöttségek tömbjét, és végül visszatér a legzsúfoltabb emelet sorszámával – tehát azzal, ahol a legtöbb vendég van éppen!

Megoldás

typedef struct Vendeg {
    char nev[50+1];
    int szobaszam;
} Vendeg;

int emelet(Vendeg v) {
    return v.szobaszam / 100;
}

Vendeg *keres(Vendeg *vendegek, int meret, char *nev) {
    int i;
    for (i = 0; i < meret; ++i)
        if (strcmp(vendegek[i].nev, nev) == 0)
            return &vendegek[i];
    return NULL;
}

void betoltottseg(Vendeg *vendegek, int meret, int *szintek) {
    int i;
    for (i = 0; i <= 7; ++i)
        szintek[i] = 0;
    for (i = 0; i < meret; ++i)
        szintek[emelet(vendegek[i])] += 1;
}

int legtobb_vendeg(Vendeg *vendegek, int meret) {
    int szintek[8];
    betoltottseg(vendegek, meret, szintek);
    int i, max = 0;
    for (i = 1; i < 8; ++i)
        if (szintek[i] > szintek[max])
            max = i;
    return max;
}

Pontozás, beugró értékelési szempontjai:

  • BEUGRÓ: legyen struktúra, tartalmazzon egy sztringet és egy egész számot. (Ha 50-es a karaktertömb, akkor beugrónak elfogadható.) Legyen emelet függvény, amelynek a fejléce legyen rendben.
  • 1 p, struktúra (typedef nem kell). Ha 50 a tömb mérete, nem 51, nem jár a pont.
  • 1 p, emelet(). Simán szobaszám / 100, mert egész osztás van.
  • 2 p, keresés: 1 p algoritmus, 1 p sztring összehasonlítás (strcmp).
  • 3 p, betöltöttség: 1 p fejléc, 1 p nullázás, 1 p hisztogram. A vendégtömb méretét át kell adni, a szinttömb méretét nem, de nem baj, ha ott van. A függvény visszaadhatja az int *szintek-et, de fölösleges. Nem a függvény dolga létrehozni a szintek tömbjét!
  • 3 p, legtöbb vendég: 1 p fejléc és visszatérési érték, 1 p tömb létrehozása + betoltottseg() hívása, 1 p maximum keresése. A maximum keresésében az indexet kell megjegyezni, mert az adja az emelet számát.

Súlyos hibák:

  • Tömbök/pointerek keverése. Pl. nem lehet a betöltöttség függvényben a szintek tömbjének létrehozása.
  • Tömb végén NULL, EOF és hasonló konstansok feltételezése.
  • Túlindexelés.

Továbbá az általános pontozási irányelvek.

Rendezés

BEUGRÓ: Írj függvényt, amely képes megcserélni két paraméterként kapott, valós típusú változó tartalmát!

Írj függvényt, amely paraméterként egy valósakat tartalmazó tömböt vesz át, továbbá két indexet, amelyek egy tartomány elejét és végét határozzák meg! Térjen vissza a függvény a megadott tartományból a legkisebb elem indexével!

Írj függvényt, amely paraméterként egy valós tömböt vesz át, és az előbbi két függvényt is felhasználva növekvő sorba rendezi a tömb számait!

Egészítsd ki ezeket egy főprogrammal, amely létrehoz egy 5 elemű tömböt, rendezi azt, majd kiírja a tömbelemeket sorszámozva!

Megoldás

#include <stdio.h>

void csere(double *x, double *y) {
    double temp = *x;
    *x = *y;
    *y = temp;
}

int minindex(double *tomb, int mettol, int meddig) {
    int min = mettol, i;
    for (i = mettol+1; i <= meddig; ++i)
        if (tomb[i] < tomb[min])
            min = i;
    return min;
}

void rendez(double *tomb, int meret) {
    int i;
    for (i = 0; i < meret-1; ++i) {
        int min = minindex(tomb, i, meret-1);
        if (min != i)
            csere(&tomb[i], &tomb[min]);
    }
}

int main(void) {
    double tomb[5] = {1.2, 3.5, 9.5, 3.4, 4.5};
    rendez(tomb, 5);
    int i;
    for (i = 0; i < 5; ++i)
        printf("%d. %g\n", i, tomb[i]);
    return 0;
}

Pontozás:

  • BEUGRÓ: cím szerinti paraméterek, tehát pointerek legyenek a cserés függvényben. Ha csere(tomb, i1, i2) alakú, beugrónak elfogadható, mert működni fog, de a pont nem fog járni érte, mert nem ez volt a feladat. A függvénytörzsben a pointereket dereferálni kell.
  • 3 p csere: 1 p fejléc, 1 p segédváltozó és pointerek helyes kezelése, 1 p háromlépéses csere.
  • 2 p minindex: 1 p fejléc és visszatérési érték, 1 p algoritmus. A tömb méretét nem kell átvenni, a két index meghatározza a tartományt. A tartomány másképp is kezelhető; pl. jobbról nyílt intervallum (nem volt meghatározva). A mintamegoldásban mindkét végén zárt az intervallum. A keresés nem építhető double minval = 0-ra, mert lehet negatív szám is a tömbben. A keresésre akkor jár a pont, ha csak egyszer megy végig a tömbön.
  • 3 p rendezés: 1 p ciklus, 1 p minindex hívása, 1 p csere hívása. A segédfüggvényeket használni kell, csak akkor jár a pont (így kérte a feladat). A határok kezelése a rendező és a minimumkereső függvényben meg kell egyezzen. A tömb méretét át kell venni, különben egyik pont sem jár, ahol használni kellene. A cserénél az if() elmaradhat. Az egész rendezés akár két sor is lehetne: for (...) { csere(... minindex(...)); }.
  • 2 p főprogram: 1 p tömb létrehozása és rendezése, 1 p számozott kiírás.

Továbbá az általános pontozási irányelvek.

2. Második NZH – minta

Gondolatjelek

Írj programot, amely a szabványos bemeneten érkező szövegből kitörli a gondolatjelek közötti részeket – olyanokat, mint ez –, és úgy írja ki a szabványos kimenetére! A gondolatjelet az különbözteti meg a kötőjeltől, hogy előtte egy szóköz van.

Rajzold meg az ehhez tartozó állapot- és tevékenységtáblát, és abból kiindulva írd meg a programot! BEUGRÓ: a programnak állapotgépesnek kell lennie.

Megoldás

szóköz mínusz többi
szöveg →lehet ki: c ki: c
lehet →gondolat ki: szóköz, c, →szöveg
gondolat →vége?
vége? →szöveg →gondolat
#include <stdio.h>

int main() {
    typedef enum GondolatAll { szoveg, lehet, gondolat, vege } GondolatAll;
    int c;
    GondolatAll allapot = szoveg;
    
    while ((c = getchar()) != EOF) {
        switch (allapot) {
            case szoveg:
                if (c == ' ') allapot = lehet;
                else putchar(c);
                break;
            case lehet:
                if (c == ' ') putchar(' ');
                else if (c == '-') allapot = gondolat;
                else { putchar(' '); putchar(c); allapot = szoveg; }
                break;
            case gondolat:
                if (c == ' ') allapot = vege;
                break;
            case vege:
                if (c == ' ') /* semmi */;
                else if (c == '-') allapot = szoveg;
                else allapot = gondolat;
                break;
        }
    }
    
    return 0;
}

Pontozás:

  • Bemenet sztringbe beolvasva, vagy iterációnként több karakter beolvasása: nincs meg a beugró, nem állapotgépes a megoldás.
  • Állapotgép: helyes állapotátmenetek 3p, helyes tevékenységek 1p.
  • Program: felsorolt típus 1p, beolvasás 1p, állapotgép kódolása 4p.

Továbbá az általános pontozási irányelvek.

Leghosszabb szó

Írj programot, mely a szabványos bemenetről fájl végéig olvas egy szöveget, a benne található leghosszabb szót megtalálja, és a futás végén a standard kimenetre írja! Egy szó alatt a bemeneti karakterfolyam olyan szakaszát értjük, melyben csak angol betűk vannak. A szavak bármilyen hosszúak lehetnek! (Javasolt saját függvényt írni a sztringhez karakter hozzáfűzéshez és sztring értékadáshoz.)

Megoldás

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

void ertekadas(char **hova, char *mit) {
    free(*hova);
    *hova = malloc(strlen(mit) + 1);
    strcpy(*hova, mit);
}
void hozzafuz(char **str, char mit) {
    char *uj = malloc(strlen(*str) + 2);
    sprintf(uj, "%s%c", *str, mit);
    free(*str);
    *str = uj;
}

int main() {
    char *akt = NULL, *legh = NULL;
    int c;

    ertekadas(&akt, "");
    ertekadas(&legh, "");
    while ((c=getchar()) != EOF) {
        if (isalpha(c))
            hozzafuz(&akt, c);
        else {
            if (strlen(akt) > strlen(legh))
                ertekadas(&legh, akt);
            ertekadas(&akt, "");
        }
    }
    printf("leghosszabb: %s\n", legh);
    free(akt);
    free(legh);

    return 0;
}

Pontozás:

  • főprogram: inicializálás 1p, beolvasás 1p, beolvasás ciklusfeltétele 1p, maximumkeresés 1p
  • hozzáfűzés: új terület foglalása 1p, régi felszabadítása 1p, pointer visszaadása 1p
  • értékadás: felszabadítás 1p, új terület foglalása 1p, pointer visszaadása 1p

Továbbá az általános pontozási irányelvek.

Átszállások

Szöveges fájlokban buszjáratok megállóinak neveit tároljuk, soronként egyet. A programod feladata, hogy láncolt listákba beolvassa a buszjáratok adatait, és megmondja két adott járatról, hogy át lehet-e szállni egyikről a másikra, vagy nem; és ha igen, hol.

a) Írj függvényt, amely paraméterként egy fájlnevet kap, visszatérési értéke pedig egy járat megállóinak listája (a fájlban szereplő sorrendben).

b) Írj függvényt, amely megvizsgál két, paraméterként kapott megállólistát, hogy van-e átszállási lehetőség (azonos megállónév). A függvény visszatérési értéke egy sztring legyen, amely a megálló neve.

c) Egészítsd ki mindezt teljes programmá, amelyben beolvasod a 7E.txt és az M3.txt nevű fájlokat! Ha át lehet szállni, írd ki a megálló nevét, ha nem, akkor pedig írd ki azt! Ne felejtsd el felszabadítani a memóriát, amit foglaltál!

Megoldás

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Megallo {
    char nev[60+1];
    struct Megallo *kov;
} Megallo;

Megallo *vegere(Megallo *lista, char const *ujnev) {
    Megallo *uj = (Megallo*) malloc(sizeof(Megallo));
    strcpy(uj->nev, ujnev);
    uj->kov = NULL;
    if (lista == NULL) {
        lista = uj;
    } else {
        Megallo *utolso;
        for (utolso = lista; utolso->kov != NULL; utolso = utolso->kov)
            ;
        utolso->kov = uj;
    }
    return lista;
}

Megallo *jarat_beolvas(char const *fajlnev) {
    FILE *fp = fopen(fajlnev, "rt");;
    if (fp == NULL)
        return NULL;
    char megallo[60+1];
    Megallo *jarat = NULL;
    while (fscanf(fp, " %[^\n]", megallo) != EOF)
        jarat = vegere(jarat, megallo);
    fclose(fp);
    return jarat;
}

void felszabadit(Megallo *lista) {
    while (lista != NULL) {
        Megallo *kov = lista->kov;
        free(lista);
        lista = kov;
    }
}

char const *hol_van_kozos(Megallo *j1, Megallo *j2) {
    Megallo *it1, *it2;
    for (it1 = j1; it1 != NULL; it1 = it1->kov)
        for (it2 = j2; it2 != NULL; it2 = it2->kov)
            if (strcmp(it1->nev, it2->nev) == 0)
                return it1->nev;
    return NULL;
}

int main(void) {
    Megallo *j1, *j2;
    j1 = jarat_beolvas("7E.txt");
    j2 = jarat_beolvas("M3.txt");
    if (j1 == NULL || j2 == NULL) {
        printf("Nem sikerült beolvasni valamelyik járatot\n");
    } else {
        char const *atszallas = hol_van_kozos(j1, j2);
        if (atszallas == NULL)
            printf("Nincs közös megálló\n");
        else
            printf("A közös megálló: %s\n", atszallas);
    }
    felszabadit(j1);
    felszabadit(j2);
    return 0;
}

Pontozás:

  • típus 1p.
  • lista végéhez fűzés 4p.
  • járat beolvasása hibaellenőrzéssel 4p.
  • n2 keresés 4p.
  • felszabadítás 2p, ha nincs duplikálva a kód.
  • főprogram 3p.

Továbbá az általános pontozási irányelvek.