Treść
Oryginalna treść zadania
W Znakolandii, planetę AUI#678 zamieszkują potwory. Wśród znakojadów wyróżniono kilka gatunków:
- typ „0” (tzw. cyfrojad) – ich głównym przysmakiem są cyfry, inne znaki pomijają, bo ich nie trawią, nie potrafią przestać jeść giną jeśli zjedzą naraz więcej niż 5 cyfr,
- typ „a” (tzw. literojad) – to stworzenia, które uwielbiają wszystkie litery, niestety wielkie litery szkodzą im jeśli zjedzą ich więcej niż 5 podczas jednego posiłku to giną,
- typ „*” (tzw. znakojad) – to najbardziej przerażające istoty, które potrafią zjeść dosłownie każdy znak, jednak łączenie wszystkich posiłków im szkodzi: jeśli zjedzą naraz co najmniej 5 cyfr więcej niż znaków różnych od cyfr to giną,
Potwory są wieczne głodne, dlatego jeśli odkryją nowe wolne miejsce przeprowadzą się do niego, a następnego dnia zjedzą cały dostępny posiłek. Jeśli podczas niego zginą to zostają w danym miejscu na zawsze. Po spustoszeniu regionu i w przypadku braku wolnego niepustego miejsca potwory opuszczają nie zamieszkują żadnego miejsca.
Region to zbiór miejsc, który zawiera miejsca, które mają ciąg znaków, który określa rodzaj posiłku tam się znajdującego. Drugiego dnia potwór pustoszy miejsce z całego posiłku, więc z ciągu znaków zostaje tylko ‘\0’ i od razu szuka kolejnego miejsca z posiłkiem bez żadnego potwora (żywego czy martwego).
Zadania do wykonania
- Stwórz strukturę miejsce, która będzie przechowywać rodzaj posiłku znajdującego się w nim, unikalny numer oraz to czy znajduje się tam jakiś potwór (żywy lub martwy).
- Napisz funkcję sprawdzMiejsce(), która jako argument przyjmie miejsce i sprawdzi czy nie mieszka potwór oraz czy jest tam dostępne pożywienie.
- Stwórz strukturę potwór, która będzie przechowywać imię potwora, wskaźnik do miejsca gdzie się znajduje, ile zjadł znaków do tej pory, jego gatunek oraz czy żyje.
- Napisz funkcję nakarm(), która będzie przyjmowała napis oraz potwora, która biorąc pod uwagę sposób odżywiania się gatunku doda ile znaków zjadł stwór lub go uśmierci.
- Napisz funkcję przeprowadzPotwora(), która przeniesie potwora do następnego dostępnego miejsca w regionie. Jeśli takie nie istnieje potwór nie powinien mieć przypisanego miejsca.
- Napisz funkcję wypisz potwora(), która poda w jednej linii jego imię, gatunek, ile zjadł znaków oraz czy żyje.
- Napisz funkcję main(), która:
- Wczyta liczbę n i m, n określi ile istnieje miejsc na planecie, a m określi ile istniej potworów na planecie
- Wczyta n ciągów znaków długości nie przekraczającej 40 znaków, które będą oznaczały jaki posiłek tam się znajduje
- Wczyta m potwór, gdzie jeden potwór to jedna linia tekstu o maksymalnej długości 15, gdzie ostatni znak określa gatunek potwora, ale nie należy do imienia, m-ty potwór zostać przypisany do m-tego miejsca. Zakładamy, że potwory wchodzą z pustym żołądkiem i żyje.
- Uruchom pętle, której każda iteracja będzie oznaczała jeden dzień i jeśli potwór znajduje się w niespustoszonym miejscu to go nakarmi, a jeśli w pustoszonym to go przeprowadzi do następnego wolnego miejsca. Program się kończy, gdy ostatnie miejsce zostanie spustoszone. Można założyć, że dane są poprawne i do takiego zdarzenia dojdzie.
- Na koniec program powinien wypisać stan każdego potwora.
Przykłady
Przykład 1
Dla danych:
- 2 1
- UIOFSR34HJK3
- HJBN78945%KL
- ZNAKOJAD*
otrzymamy:
- ZNAKOJAD*24ZYJE
Przykład 2
Dla danych:
- 3 2
- 74309436WerO
- UI%%R3dherf3
- HJsd78945%KL
- CYFROJAD0
- LITEROJADa
Otrzymamy:
- CYFROJAD08NIE ZYJE
- LITEROJADa14ZYJE
Dodatkowe uwagi
Program powinien prawidłowo zwalniać dynamicznie zaalokowaną pamięć.
Rozwiązanie
Poniżej znajduje się pełne rozwiązanie zadania razem z komentarzem:
- #include <iostream>
- using namespace std;
- struct miejsce{
- char* posilek; // Jaki posiłek się znajduje na miejscu
- int index; // Unikalny indeks miejsca
- bool potwor; // Czy mieszka tam potwór
- };
- struct potwor{
- char* imie; // Imię potwora
- miejsce* lokalizacja; // Wskaźnik na lokalizacje z regionu
- int znakow_zjedzonych; // Ile zostało zjedzonych znaków przez potwora
- char gatunek; // Gatunek potwowa
- bool zyje; // Czy potwór żyje
- };
- bool sprawdzMiejsce(miejsce* m);
- void nakarm(potwor* p);
- void przeprowadzPotwora(miejsce** miejsca, int n, potwor* p);
- int main () {
- // Wczytujemy miejsca w regionie
- int n, m;
- cin >> n >> m;
- cin.ignore();
- miejsce** miejsca = new miejsce*[n];
- for(int i = 0; i < n; i++){
- miejsce* m = new miejsce;
- char* data = new char[41];
- cin.getline(data, 41);
- m->index = i;
- m->potwor = false;
- m->posilek = data;
- miejsca[i] = m;
- }
- // Wczytujemy wszystkie potwory
- potwor** potwory = new potwor*[n];
- for(int i = 0; i < m; i++){
- potwor* p = new potwor;
- char* data = new char[16];
- cin.getline(data, 16);
- int dlugosc = strlen(data);
- p->gatunek = data[dlugosc-1];
- data[dlugosc-1]='\0';
- p->imie = data;
- p->lokalizacja = miejsca[i];
- p->znakow_zjedzonych = 0;
- p->zyje = true;
- potwory[i] = p;
- }
- // Wykonujemy kolejne dni iteracji
- while(miejsca[n-1]->posilek[0] != '\0'){
- for(int i = 0; i < m; i++){
- if(potwory[i]->zyje){
- nakarm(potwory[i]);
- przeprowadzPotwora(miejsca, n, potwory[i]);
- }
- }
- }
- // Wypisujemy wszystkie potwory
- for(int i = 0; i < m; i++){
- cout << potwory[i]->imie << "\t" << potwory[i]->gatunek << "\t" << potwory[i]->znakow_zjedzonych << "\t" << (potwory[i]->zyje ? "ZYJE" : "NIE ZYJE") << endl;
- }
- // Zwalniamy pamięć
- for(int i = 0; i < n; i++){
- delete[] miejsca[i]->posilek;
- }
- delete[] miejsca;
- for(int i = 0; i < m; i++){
- delete[] potwory[i]->imie;
- }
- delete[] potwory;
- system ("pause");
- return 0;
- }
- // Prawda jeśli nie mieszka żaden potwór i znajduje się tam posiłek.
- bool sprawdzMiejsce(miejsce* m){
- return (!m->potwor || m->posilek[0] != '\0');
- }
- // Karmi potwora na podstawie jego lokalizacji
- void nakarm(potwor* p){
- // W celu uproszczenia zapisu zdobywamy wksaźnik na lokalizacje
- char* napis = p->lokalizacja->posilek;
- if(p->gatunek == '0'){
- // Zliczamy ile zje cyfrojad cyfr
- int licznik = 0, i = 0;
- while(napis[i]){
- if(napis[i]>='0' && napis[i]<='9')
- licznik++;
- i++;
- }
- p->znakow_zjedzonych += licznik;
- // Jeśli zjadł więcej niż 5 cyfr to uśmierć
- if(licznik > 5){
- p->zyje=false;
- }
- } else if (p->gatunek == 'a') {
- // Zliczamy ile zje literojad mały i wielkich liter
- int licznik_a = 0, licznik_A = 0, i = 0;
- while(napis[i]){
- if(napis[i]>='a' && napis[i]<='z')
- licznik_a++;
- if(napis[i]>='A' && napis[i]<='Z')
- licznik_A++;
- i++;
- }
- p->znakow_zjedzonych += licznik_a + licznik_A;
- // Jeśli zjadł więcej niż 5 wielkich liter to uśmierć
- if(licznik_A > 5){
- p->zyje=false;
- }
- } else {
- // Zliczamy ile zje cyfr i innych znaków
- int licznik_0 = 0, licznik_ = 0, i = 0;
- while(napis[i]){
- if(napis[i]>='0' && napis[i]<='9')
- licznik_0++;
- else
- licznik_++;
- i++;
- }
- p->znakow_zjedzonych += licznik_0 + licznik_;
- // Jeśli zjadł więcej o 5 cyfr niż znaków to uśmierć
- if(licznik_0 > licznik_ + 5){
- p->zyje=false;
- }
- }
- // Zamień pierwszy znak, aby zaznaczyć, że posiłek jest pusty
- p->lokalizacja->posilek[0]='\0';
- }
- // Przeprowadza potwora do nowej wolnej lokalizacji
- void przeprowadzPotwora(miejsce** miejsca, int n, potwor* p){
- // Pobierz starą lokalizację i przywróć domyślne ustawienia zmiennych
- int indeks = p->lokalizacja->index;
- p->lokalizacja = NULL;
- miejsca[indeks]->potwor = false;
- indeks++;
- // Szukaj nowej wolnej lokalizacji
- while(!sprawdzMiejsce(miejsca[indeks]) && indeks < n){
- indeks++;
- }
- // Jeśli indeks != n to znaczy, że istnieje nowa wolna lokzalizacja w regionie
- if(indeks != n){
- p->lokalizacja = miejsca[indeks];
- miejsca[indeks]->index = true;
- }
- }