49 / 49
Feb 2024

Pierwsza sprawa:
Po co w ogóle Ci tablica? Załóżmy, że masz do napisania program co wyznacza sumę liczb podanych na wejściu (na początku jeszcze liczba zestawów).
Twój program by wyglądał:

...
cin >> data;
for (int i = 0; i < data; ++i) {
cin >> tablica[i][0] >> tablica[i][1];
}
for (int i = 0; i < data; ++i) {
cout << tablica[i][0] + tablica[i][1];
}

Podczas gdy powinien wyglądać tak:

...
cin >> data;
for (int i = 0; i < data; ++i) {
cin >> a >> b;
cout << a + b;
}

Inaczej mówiąc: nie tabelaryzuj tych danych tylko po to by po nich przejsć. Po prostu operuj na nich przy wczytywaniu.

Druga sprawa:
W twoim kodzie jest: jeśli A==B to weź max(A,B)/2. Po co ten max skoro są równe?

Trzecia sprawa:
Czy na pewno chcąc sprawdzić czy liczba się przez coś dzieli musisz brać counter liczący (w dół) od mniejszej z nich? Wydaje mi się (nie wydaje mi się, tak z grzeczności piszę), że można sprawdzić o wiele mniejszy zbiór kolejnych dzielników.

Czwarta sprawa:
Masz jakiś błąd wykonania:
błąd7

Same odpowiedzi wyglądają poprawnie.

Dziękuję za odpowiedź!
Poprawiłem następujące punkty: 1, 2 i 4.
Co do trzeciej sprawy - niekiedy (szczególności dla małych wartości <20) zachodzi własność, że wynik A%B często jest równy dugości boku kwadratu. Ale problem jest już znaczący przy liczbach >10^5, gdy przykładowo A%B = 1456, a bok kwadratu wyniesie np. 5. Wówczas algorytm musi ,piłować " w dół do 5. Z kolei stosując algorytm liczący ,w górę" będzie musiał przejść cały przedział, by odnaleźć maksymalnie duży kwadrat (chodzi mi o to, że znajdzie kwadrat dla 1, 5, 7, … ale i tak będzie musiał liczyć, aż do A%B, by sprawdzić, że np dla 1000 nie zachodzi optymalniejszy warunek).
W każdym razie dziękuję i będę dalej próbować. Skoncentruję się by ten zakres jeszcze bardziej zawężyć.

I jeszcze taki przykład 37600%36425=1175 i właśnie 1175 jest największym możliwym bokiem kwadratu. Tak więc, jak nadmieniłem, w niektórych przypadkach, trzeba będzie sprawdzić cały zakres <0,1175>

Ciekawe jest Twoje podejście (w sensie rozkmina zadania). Tak, są takie przypadki, ale stwierdzenie, że trzeba będzie sprawdzić cały zakres <0,1175> jest nieprawdziwe. Dla przypadku 37600 36425 kilkoma obliczeniami da się dojść do wartości 1175, a gdyby warunek sprawiający “korzystność” tego przypadku nie występował to i tak bym sprawdził zaledwie <2,~200> (nie znaczy, że wynik będzie w tym przedziale), ale to już wystarczająco się nagadałem. Teraz kombinuj.

Minęło kilka dni i w mojej ocenie zoptymalizowałem kod maksymalnie.
Tj. zastosowałem ulepszony alg. Euklidesa a nie klasyczny alg, a także rozpatrzyłem oddzielny przypadek dla kwadratu, by maksymalnie proces przyśpieszyć.
Tu jest mój kod:


Sędzia jest natomiast innego zdania i twierdzi, że kod nadal nie jest maksymalnie zoptymalizowany - przekroczono limit czasu. Czy mogę prosić kogoś o wskazówkę?
Z góry dziękuję!

Jest miejsce na optymalizację dla fragmentu:

    while(b%(a/counter)!=0)
        counter++;
    a = a/counter;

Dla liczby pierwszej (a=b=duża liczba pierwsza) pętla while wykona się mniej więcej a/2 razy. To jest wciąż za dużo razy.

Dzięki wielkie pawoj20 i redysz, poprawiłem i zaakceptowało mi :smiley:
Życzę powodzenia pozostałym i wytrwałości, sam bym zakwalifikował to zadanie jako średnie.

3 years later

Witam. Napisałem program do tego zadania, działa w visual studio 2019 i w codeblocks, a na Ideone dla pewnych konkretnych przypadków nie działa, nie rozumiem dlaczego. Może za słabo rozumiem rekurencję, porobiłem “Print it” i rezultat też mnie zadziwił. Oto kod, z góry dziękuję za wyjaśnienie/rozjaśnienie:) https://ideone.com/xPWmmV7

  1. Jeśli masz VS to zapewne masz też / możesz mieć łatwo debugger. Warto z niego skorzystać. Ja mam się gorzej, bo nigdy nie chce mi się ogarniać na SPOJa armat większych niż Vim i kompilator.

  2. Jeżeli argument przekazany do funkcji nie jest modyfikowany, oznaczaj go jako const. Taka drobna poprawka jakościowa i dobry nawyk na przyszłość. Np:

    NWD(const int Liczba1, const int Liczba2)
    
  3. zaprzyjaźnij się z assertem. Pomaga przy debuggowaniu na SPOJu. Aczkolwiek nie w tym przypadku - również uwaga ogólna. Na start IFy są jak najbardziej ok, by się sprawdzić i pozytywnie zaskoczyć, że kod działa. Ale z czasem zarządzanie nimi staje się męczące. I nieprofesjonalne.

  4. W tym przypadku wskazane jest, żebyś rozpisał na kartce co się dzieje w kodzie krok po kroku. Trochę to zajmie, ale łatwo namierzysz błąd. W 16 linii. Co robisz ze zwracanym wynikiem?

No tak, przecież funkcja rekurencyjna musi wrócić z zagnieżdżenia :man_facepalming: Assert jak zrozumiałem jest bardziej rozbudowaną metodą printit? A czego lepiej używać zamiast ifów ?:thinking:

"Niestety, Pan Jan nie jest mocny z matematyki …"i dla niego, kwadrat, to też prostokąt ;-). A może odwrotnie, to matematycy nie potrafią odróżnić kwadrata od prostokąta? :wink:

Jak nie musisz, nie używaj endl’a. Lepiej '\n'
Ja raczej deklaruję zmienne, przed (poza) pętlą…

IFy są ok. Chodzi mi o to, żeby zamiast robić ify w debuggu typu:

if(x == 0) cout << "Oj, x to zero!";

oraz pisać same couty upewniające Cię co do logiki w kodzie typu:

void f(int x, int y) {
   cout << "Wolam f dla " << x << " oraz " << y << endl;
   //...
}

klepać, jak już coś, ewentualne zabezpieczenia np. tak:

assert(x != 0);

Porównaj: https://ideone.com/idgbib1 z https://ideone.com/vPuShA1

Zapamiętam assert, jako bajer na przyszłość :slightly_smiling_face: Dzięki cout mogę łatwo wyświetlić wartość zmiennych, i nr linii też. Ale warto uczyć się nowych rzeczy więc skorzystam. A te ify w main to nie były do debugu, na początku pomyślałem że NWD nie działa jeśli drugi argument jest większy niż pierwszy.
A dlaczego lepsze ‘\n’ niż endl? :thinking: Co do zmiennych, w tym przykładzie akurat to nie ma znaczenia, ale często w takiej pętli “testów” zapominałem zerować jakieś zmienne i kolejne testy były nie poprawne. A tak się same zerują. Jeśli mamy zagnieżdżoną pętlę for(), to “j” w wewnętrznej pętli też jest wiele razy(dokładniej “i” razy :slightly_smiling_face:) inicjowane.

Miałeś SIGABORT? (sigsegv)
Masz przekroczenie limitu czasu?
Będziesz miał WA - błędną odpowiedź.
Więc po prostu skorzystaj z podpowiedzi :wink:

Inicjowanie == zerowanie !? ?! No no :slight_smile:

Miałem sigsegv przez głupotę którą wskazał mr. T, limit czasu oczywiście też, ale jakieś zadania z dzielnikami na spoju już rozwiązałem, i przypomniało mi się jak to skrócić (w czasie). I oczywiście masz rację, inicjacja nie żeruje zmiennych, chyba że dopisze się do niej =0. A jeśli Wynik+=Podwynik to chyba trzeba ją tak zainicjować, i wtedy nie trzeba pamiętać o żerowaniu jej na końcu pętli. Nie wiem czy to dobrze czy źle, dopiero się uczę, to chyba widać. Tak samo nie wiem dlaczego lepiej używać /n niż endl. Wiem że czepiam się szczegółów, co potrafi być irytujące, ale uważam je za ważne, tym bardziej w kodowaniu🙂

Skoro się uczysz: koncepcja default initialization jest Ci znana?

Co to za (nowa?, twoja?) koncepcja?
W swoim programie na ideone, usiłujesz udowodnić, że zmienne globalne są inicjalizowane wartością zero? W taki sposób niczego nie udowadniasz, a nawet gdybyś użył zmiennych lokalnych, to też niczego, w taki sposób nie dowiedziesz. A że zmienne globalne są tak inicjalizowane, to chyba każdy wiedzieć powinien?
I to jest ta default initialization koncepcija? Wprawdzie nigdy o czymś takim jeszcze nie słyszałem, ale człowiek uczy się całe życie.

Wracając do zadania.
Dla takiej konstrukcji, wykorzystanej przez pytającego, w tym zadaniu, wewnątrz pętli:

cin >> bok1 >> bok2;

zmienne bok1, bok2, nie muszą być zerowane. Dowolna, poprzednia zwartość jest za każdym razem [za pierwszym też], nadpisywana, zamazywana nowymi, wczytanymi wartościami.

https://en.cppreference.com/w/cpp/language/default_initialization1

Raczej jedynie pokazać, że coś takiego istnieje.

Ale początkujący może mieć problemy z nazwaniem mechanizmu i wyszukaniem jego szczegółów przy bardziej złożonych konstrukcjach (czy zmienne lokalne też są tak inicjalizowane? Czy to faktycznie default-initialization czy zero-initialization czy może obie odpowiedzi są poprawne? Czy te zmienne w moim kodzie są statyczne?). Zwłaszcza, jeżeli na start nauczy się uproszczeń jako pewników i nie dowie o istnieniu jakiejkolwiek poważnej dokumentacji.

Nie wiedziałem że zmienne globalne są tak inicjalizowane, wiem za to że lepiej ich nie używać, żeby np. nie zalegały w pamięci, i ktoś tam o jakimś ogniu czy piekle przy nich wspominał.

Oczywiście że wiem co oznacza ta instrukcja, przedtem pisałem:

Oczywiście gdy deklaruję z inicjacją. Nie widzę w tym nic strasznego bo w wielu przykładach jest pokazywane takie coś:
for(int i=1; i<=9; i++){
for(int j=0; j<=9; j++)

}
A nie np. takie
int i, j;
for(i=1; i<=9; i++){
for(j=0; j<=9; j++)

}
To oczywiście tylko moja(uczącej się osoby) opinia