{"id":"informatyka-2016-maj-matura-rozszerzona/zad/5.4","paper_id":"informatyka-2016-maj-matura-rozszerzona","number":"5.4","points":3,"ptype":"open","subject":"informatyka","category":"matura","year":2016,"month":"maj","level":"rozszerzona","text":"Kontekst - patrz zadanie 5.1.\n\nPodaj nazwiska i imiona studentów, którzy **nie** mieszkają w pokojach w miasteczku akademickim. Listę posortuj alfabetycznie wg nazwisk.","answer":null,"answer_text":null,"solution":"## Poprawna odpowiedź\n\n| Nazwisko | Imię |\n| DYLAG | JACEK |\n| NAJDA | PIOTR |\n| PIETRASZEWSKI | STEFAN |\n| SIECZKOWSKI | MACIEJ |\n| ZALESKA | JULIA |\n\n## Sposób 1 - SQL (LEFT JOIN albo NOT IN)\n\n**Idea:** studenci, których PESEL **nie występuje** w meldunek.txt (= nie mieszkają w miasteczku).\n\n**Wersja 1 - LEFT JOIN + WHERE NULL:**\n```sql\nSELECT s.nazwisko, s.imie\nFROM studenci s\nLEFT JOIN meldunek m ON s.pesel = m.pesel\nWHERE m.pesel IS NULL\nORDER BY s.nazwisko;\n\n**Wersja 2 - NOT IN:**\n```sql\nSELECT nazwisko, imie\nFROM studenci\nWHERE pesel NOT IN (SELECT pesel FROM meldunek)\nORDER BY nazwisko;\n\n**Wersja 3 - NOT EXISTS (najbezpieczniej z NULLami):**\n```sql\nSELECT s.nazwisko, s.imie\nFROM studenci s\nWHERE NOT EXISTS (\nSELECT 1 FROM meldunek m WHERE m.pesel = s.pesel\n)\nORDER BY s.nazwisko;\n\n## Sposób 2 - Python\n\n```python\n# Wczytaj studentów\nstudenci = {}\nwith open('studenci.txt', encoding='windows-1250') as f:\nnext(f)\nfor linia in f:\ncz = linia.strip().split('\\t')\nif len(cz) >= 3:\npesel, nazwisko, imie = cz[0], cz[1], cz[2]\nstudenci[pesel] = (nazwisko, imie)\n\n# Wczytaj PESELe zameldowanych\nzameldowani = set()\nwith open('meldunek.txt', encoding='windows-1250') as f:\nnext(f)\nfor linia in f:\ncz = linia.strip().split('\\t')\nif len(cz) >= 1:\nzameldowani.add(cz[0])\n\n# Znajdź spoza miasteczka\nspoza = [studenci[p] for p in studenci if p not in zameldowani]\nspoza.sort() # sortuje alfabetycznie wg nazwiska (pierwszy element krotki)\n\nfor nazwisko, imie in spoza:\nprint(f\"{nazwisko} {imie}\")\n\n**Wynik (5 osób):**\nDYLAG JACEK\nNAJDA PIOTR\nPIETRASZEWSKI STEFAN\nSIECZKOWSKI MACIEJ\nZALESKA JULIA\n\n## Sposób 3 - arkusz kalkulacyjny\n\n1. W studenci.txt: kolumna D z formułą sprawdzającą obecność w meldunek.txt:\n`=JEŻELI(JEST.BŁĄD(WYSZUKAJ.PIONOWO(A2; meldunek!A:A; 1; FAŁSZ)); \"SPOZA\"; \"W KAMPUSIE\")`\n2. Filtruj kolumnę D = \"SPOZA\" → uzyskaj listę nazwisk.\n3. Sortuj kolumnę B (nazwisko) rosnąco.\n\n## Reference informatyczny - relacje NOT IN / NOT EXISTS / LEFT JOIN\n\n> Reference - Antyjoin (znajdowanie braku dopasowania):\n> - **LEFT JOIN + WHERE NULL**: najbardziej czytelne; bierzemy wszystkie z lewej, łączymy z prawą; brak dopasowania → NULL w polach prawej; filtrujemy.\n> - **NOT IN (podzapytanie)**: prosta składnia, ale **uwaga na NULLe** - `pesel NOT IN (1, 2, NULL)` zwróci `unknown` zamiast `true`!\n> - **NOT EXISTS**: najwydajniejsze i bezpieczne z NULLami.\n>\n> Reference - JOIN-y:\n> - **INNER JOIN** - tylko dopasowane rekordy.\n> - **LEFT JOIN** - wszystkie z lewej + dopasowane z prawej.\n> - **FULL JOIN** - wszystkie z obu (rzadko w CKE).\n> - **CROSS JOIN** - iloczyn kartezjański.\n>\n> Reference - ORDER BY:\n> - Domyślnie ASC (rosnąco).\n> - Sortowanie alfabetyczne stringów jest leksykograficzne (UPPER < lower w ASCII).\n\n## Schemat oceniania CKE\n\n> Klucz CKE (zadanie 5.4, max 3 pkt):\n> - **3 pkt** - wszystkie 5 nazwisk poprawnie + posortowane\n> - **2 pkt** - wszystkie 5 nazwisk poprawnie, ale BEZ sortowania\n> - **0 pkt** - niepełna lub błędna\n>\n> **Uwaga: NIE PRZYZNAJE SIĘ 1 PUNKTU** (binarnie: kompletna lista vs nic).\n\n## Typowe pułapki\n\n- **Posortowanie wg imienia** zamiast nazwiska - strata 1 pkt.\n- **Pominięcie 1 z 5 osób** - strata 3 pkt (niepełna = 0).\n- **Wpisanie osób Z miasteczka** zamiast SPOZA - odwrotny wynik (większość studentów).\n- **NOT IN z NULL** - jeśli w podzapytaniu pojawi się NULL, NOT IN zwraca pustą listę. Lepiej NOT EXISTS.\n- **Sortowanie nieczułe na wielkość znaków** - w CKE nazwiska są UPPERCASE, więc problem nie powinien wystąpić, ale w innych zadaniach `COLLATE NOCASE`.\n- **Włączenie nagłówka do wyniku** - w SQL nigdy nie ma, w arkuszu - uwaga.\n\n## Złożoność obliczeniowa\n\n- Wczytanie obu plików: O(S + M).\n- Sprawdzenie obecności w set: O(1) per query.\n- Sortowanie 5 wyników: O(k log k) gdzie k = 5.\n- **Łącznie: O(S + M).**","image":null,"solution_image":null,"topics":null,"page_from":null,"source":"maturazai","answer_source":null,"answer_text_source":null,"solution_source":"maturazai","text_source":"maturazai","source_label":"Informatyka · Matura · maj 2016 (rozszerzona)","subject_label":"Informatyka","category_label":"Matura","text_html":"<p>Kontekst - patrz zadanie 5.1.</p>\n<p>Podaj nazwiska i imiona studentów, którzy <strong>nie</strong> mieszkają w pokojach w miasteczku akademickim. Listę posortuj alfabetycznie wg nazwisk.</p>","solutions":[{"source":"maturazai","label":"maturazai.pl (AI)","kind":"text","html":"<h4>Poprawna odpowiedź</h4>\n<p>| Nazwisko | Imię |<br>| DYLAG | JACEK |<br>| NAJDA | PIOTR |<br>| PIETRASZEWSKI | STEFAN |<br>| SIECZKOWSKI | MACIEJ |<br>| ZALESKA | JULIA |</p>\n<h4>Sposób 1 - SQL (LEFT JOIN albo NOT IN)</h4>\n<p><strong>Idea:</strong> studenci, których PESEL <strong>nie występuje</strong> w meldunek.txt (= nie mieszkają w miasteczku).</p>\n<p><strong>Wersja 1 - LEFT JOIN + WHERE NULL:</strong><br>```sql<br>SELECT s.nazwisko, s.imie<br>FROM studenci s<br>LEFT JOIN meldunek m ON s.pesel = m.pesel<br>WHERE m.pesel IS NULL<br>ORDER BY s.nazwisko;</p>\n<p><strong>Wersja 2 - NOT IN:</strong><br>```sql<br>SELECT nazwisko, imie<br>FROM studenci<br>WHERE pesel NOT IN (SELECT pesel FROM meldunek)<br>ORDER BY nazwisko;</p>\n<p><strong>Wersja 3 - NOT EXISTS (najbezpieczniej z NULLami):</strong><br>```sql<br>SELECT s.nazwisko, s.imie<br>FROM studenci s<br>WHERE NOT EXISTS (<br>SELECT 1 FROM meldunek m WHERE m.pesel = s.pesel<br>)<br>ORDER BY s.nazwisko;</p>\n<h4>Sposób 2 - Python</h4>\n<p>```python</p>\n<h3>Wczytaj studentów</h3>\n<p>studenci = {}<br>with open(&#x27;studenci.txt&#x27;, encoding=&#x27;windows-1250&#x27;) as f:<br>next(f)<br>for linia in f:<br>cz = linia.strip().split(&#x27;\\t&#x27;)<br>if len(cz) &gt;= 3:<br>pesel, nazwisko, imie = cz[0], cz[1], cz[2]<br>studenci[pesel] = (nazwisko, imie)</p>\n<h3>Wczytaj PESELe zameldowanych</h3>\n<p>zameldowani = set()<br>with open(&#x27;meldunek.txt&#x27;, encoding=&#x27;windows-1250&#x27;) as f:<br>next(f)<br>for linia in f:<br>cz = linia.strip().split(&#x27;\\t&#x27;)<br>if len(cz) &gt;= 1:<br>zameldowani.add(cz[0])</p>\n<h3>Znajdź spoza miasteczka</h3>\n<p>spoza = [studenci[p] for p in studenci if p not in zameldowani]<br>spoza.sort() # sortuje alfabetycznie wg nazwiska (pierwszy element krotki)</p>\n<p>for nazwisko, imie in spoza:<br>print(f&quot;{nazwisko} {imie}&quot;)</p>\n<p><strong>Wynik (5 osób):</strong><br>DYLAG JACEK<br>NAJDA PIOTR<br>PIETRASZEWSKI STEFAN<br>SIECZKOWSKI MACIEJ<br>ZALESKA JULIA</p>\n<h4>Sposób 3 - arkusz kalkulacyjny</h4>\n<ol><li>W studenci.txt: kolumna D z formułą sprawdzającą obecność w meldunek.txt:</li></ol>\n<p><code>=JEŻELI(JEST.BŁĄD(WYSZUKAJ.PIONOWO(A2; meldunek!A:A; 1; FAŁSZ)); &quot;SPOZA&quot;; &quot;W KAMPUSIE&quot;)</code></p>\n<ol><li>Filtruj kolumnę D = &quot;SPOZA&quot; → uzyskaj listę nazwisk.</li><li>Sortuj kolumnę B (nazwisko) rosnąco.</li></ol>\n<h4>Reference informatyczny - relacje NOT IN / NOT EXISTS / LEFT JOIN</h4>\n<blockquote>Reference - Antyjoin (znajdowanie braku dopasowania):<br>- <strong>LEFT JOIN + WHERE NULL</strong>: najbardziej czytelne; bierzemy wszystkie z lewej, łączymy z prawą; brak dopasowania → NULL w polach prawej; filtrujemy.<br>- <strong>NOT IN (podzapytanie)</strong>: prosta składnia, ale <strong>uwaga na NULLe</strong> - <code>pesel NOT IN (1, 2, NULL)</code> zwróci <code>unknown</code> zamiast <code>true</code>!<br>- <strong>NOT EXISTS</strong>: najwydajniejsze i bezpieczne z NULLami.<br><br>Reference - JOIN-y:<br>- <strong>INNER JOIN</strong> - tylko dopasowane rekordy.<br>- <strong>LEFT JOIN</strong> - wszystkie z lewej + dopasowane z prawej.<br>- <strong>FULL JOIN</strong> - wszystkie z obu (rzadko w CKE).<br>- <strong>CROSS JOIN</strong> - iloczyn kartezjański.<br><br>Reference - ORDER BY:<br>- Domyślnie ASC (rosnąco).<br>- Sortowanie alfabetyczne stringów jest leksykograficzne (UPPER &lt; lower w ASCII).</blockquote>\n<h4>Schemat oceniania CKE</h4>\n<blockquote>Klucz CKE (zadanie 5.4, max 3 pkt):<br>- <strong>3 pkt</strong> - wszystkie 5 nazwisk poprawnie + posortowane<br>- <strong>2 pkt</strong> - wszystkie 5 nazwisk poprawnie, ale BEZ sortowania<br>- <strong>0 pkt</strong> - niepełna lub błędna<br><br><strong>Uwaga: NIE PRZYZNAJE SIĘ 1 PUNKTU</strong> (binarnie: kompletna lista vs nic).</blockquote>\n<h4>Typowe pułapki</h4>\n<ul><li><strong>Posortowanie wg imienia</strong> zamiast nazwiska - strata 1 pkt.</li><li><strong>Pominięcie 1 z 5 osób</strong> - strata 3 pkt (niepełna = 0).</li><li><strong>Wpisanie osób Z miasteczka</strong> zamiast SPOZA - odwrotny wynik (większość studentów).</li><li><strong>NOT IN z NULL</strong> - jeśli w podzapytaniu pojawi się NULL, NOT IN zwraca pustą listę. Lepiej NOT EXISTS.</li><li><strong>Sortowanie nieczułe na wielkość znaków</strong> - w CKE nazwiska są UPPERCASE, więc problem nie powinien wystąpić, ale w innych zadaniach <code>COLLATE NOCASE</code>.</li><li><strong>Włączenie nagłówka do wyniku</strong> - w SQL nigdy nie ma, w arkuszu - uwaga.</li></ul>\n<h4>Złożoność obliczeniowa</h4>\n<ul><li>Wczytanie obu plików: O(S + M).</li><li>Sprawdzenie obecności w set: O(1) per query.</li><li>Sortowanie 5 wyników: O(k log k) gdzie k = 5.</li><li><strong>Łącznie: O(S + M).</strong></li></ul>"}]}