Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Zadanie "Skrzaty" z VI OIG

Ostatnio zmodyfikowano 2013-10-09 18:44
Autor Wiadomość
withelm
» 2013-09-22 02:02:00
Racja takie wynik sa na maszynie 64 bitowej, a raczyłes sprawdzić jakie sa wyniki dla maszyny 32 bitowej? Bo to jest DUZA roznica, btw, nie rob sobie juz jaj i pomyśl, a nie probojesz wbijc zadanie jakimis brutami. Może w "prawdziwym życiu" przydaje się takie kompitowanie (nie wiem), ale na OI i innych konkursach tego typu nie zajedziesz daleko takim rozumowaniem.

Btw, i tak jestem pod wrazeniem, że wbiłes bruta na 80 pkt.
P-92446
Chumanista
Temat założony przez niniejszego użytkownika
» 2013-09-22 09:34:26
@withelm
Raczyłem, to była maszyna wirtualna Ubuntu 32bit w VMware.
Na 64bit takie pomiary byłyby oczywiście bez sensu.
Jeśli nadal upiera się pan przy swoim mogę przedstawić zestawienie ilości cykli procesora dla obu metod i pokazać wyższość 64bit.
BTW: test na main.edu.pl potwierdza te wyniki.
@Admixior
Moje zdrowie jest w jak najlepszym porządku, dziękuję za troskę.
Nie wiem, czy na tym forum kwestionowanie słów admina jest karalne, ale co tam:
Pana omówienia mojego kodu nie rozumiem zupełnie, ale zadam jedno pytanie: o której pętli pan mówi?
Wrzuciłem log z valgrinda z annotate z cyklami procesora:
           .    .    .             .         .    .         .    .    .  static inline void setFalse(uint64_t *a, int pos)
a więc inline jest ok.
Przypominam, że kompiluję tym samym co oni.
Jak rozumiem w 3-krokowym rozwiązaniu chodziło o mniej-więcej taki kod:
C/C++
#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstdlib>

using namespace std;

void inline vadd( vector < bool > & a, vector < bool > & b )
{
   
}
int main( void )
{
    int iloscSkrzatow, czasDoSnu;
    scanf( "%i", & iloscSkrzatow );
    scanf( "%i", & czasDoSnu );
    vector < bool > safe;
    safe.resize( iloscSkrzatow, false );
    safe[ 0 ] = true;
    vector < bool > safeT;
    safeT.resize( iloscSkrzatow, true );
    for( int i = 0; i < czasDoSnu; i++ )
    {
        replace( safeT.begin(), safeT.end(), false, true );
        int length;
        scanf( "%i", & length );
        for( int j = 0; j < length; j++ )
        {
            int temp;
            scanf( "%i", & temp );
            safeT[ temp - 1 ] = false;
        }
        for( unsigned int m = 0; m < iloscSkrzatow; m++ )
        {
            if( safe[ m ] && safeT[ m ] )
            {
                for( unsigned int i = 0; i < iloscSkrzatow; i++ )
                     safe[ i ] = safeT[ i ] || safe[ i ];
               
                break;
            }
        }
        if( safe[ iloscSkrzatow - 1 ] )
        {
            printf( "%i\n", i );
            //system("pause"); //DEBUG
            return 0;
        }
    }
    printf( "%i\n", czasDoSnu );
    //system("pause"); //DEBUG
}
Jest od wolniejszy i na 60pkt.
Jeśli nadal pan nie wierzy mogę sprawdzić valgrindem, ale w skrócie:
1 operacja binarna < 64 dostępy do tablicy.
Oszczędzanie pamięci w ten sposób byłoby bez sensu a jaki pisałem moje zdrowie jest w jak najlepszym porządku, dziękuję za troskę.
P-92448
ison
» 2013-09-22 12:38:02
Jaki jest sens w żyłowaniu bruta na trochę więcej pkt? To, że brut po tego typu optymalizacjach dostaje więcej niż 40 pkt to tylko dzięki temu, że ktoś kto układał zadanie ustawił dużo większe limity czasowe (lub odpowiednio mniejsze testy).
P-92467
Chumanista
Temat założony przez niniejszego użytkownika
» 2013-09-22 15:49:38
Rozumiem.
W takim razie jak to zrobić inaczej?
P-92485
Admixior
» 2013-09-22 20:26:50
Trzy uwagi:
1. Jeżeli możesz używać tablic o stałym rozmiarze to je używaj. Rezerwowanie elementów TRWA wywoływanie konstruktorów dla wektorów trwa(może w tym przypadku nie wybitnie długo ale trwa.

2. jeżeli to możliwe to zrób wszystko i stań na głowie, aby tablice były domyślnie "wyfalsowane" i zarezerwowane globalnie (nie wykonujesz memset(wykonywany podczas replace w wektorze) co skraca działanie szczególnie dla duzej liczby obiektów), napisz algorytm tak żeby stan początkowy był osiągnięty jak najmniejszym nakładem czasu.

3. Zmniejsz złożoność programu. Jak spojrzysz na pętlę to zobaczysz że masz złożoność "liczba godzin" * "liczba skrzatów" do kwadratu. Fakt że to zadanie ma złożoność do kwadratu oznacza że jest coś źle. Można to zrobić znacznie szybciej.

Półrozwiązanie:
Tworzysz tablice bool dla każdego(max) (false - nie odwiedzone).
pobierasz ilość skrzatów itp.

Klucz:
Zauważ że
[...] oznaczająca liczbę obserwowanych stanowisk oraz  uporządkowanych rosnąco liczb całkowitych ze zbioru  oznaczających numery obserwowanych stanowisk.
 Więc wystarczy porównywać aktualnie przetwarzanego skrzata (a przetwarzasz je co każde okrążenie pętli) czy czasem nie patrzy na niego smok, jak nie to zaznaczasz że tam skrzat już dotarł (true do tablicy).
Jeżeli się patrzy to go pomijasz(nie oznaczasz ani true ani też false bo już wcześniej mógł być odwiedzony) i pobierasz ze strumienia następnego na kogo się patrzy (chyba że ta grupa na kogo się patrzy się skończyła to oznaczasz wszystkich do końca że są odwiedzeni)

Co każde przetworzenie grupy sprawdzasz czy w tablicy jest już dany element oznaczony jako true(tam gdzie ma dotrzeć). Jeśli jest to koniec wypisujesz wynik a pozostałe dane wejściowe pomijasz jeżeli nie to kolejna pętla.

Jeśli sen smoka się zaczął to koniec i tyle potrzeba.
KONIEC :)

Złożoność pamięciowa 1MB (tyle ile skrzatów * bool)
Złożoność obliczeniowa (max): k1+k2+k3+...+km <= 2mln, więc (liniówka zależna od ilości godzin "aktywności" smoka razy ilość skrzatów): O(m*n)
P-92508
DejaVu
» 2013-09-23 11:30:03
Ogłoszenie parafialne: "Proszę szanować się nawzajem i nie pisać słów, których sami nie chcielibyśmy czytać.".
P-92521
Chumanista
Temat założony przez niniejszego użytkownika
» 2013-09-27 10:55:34
Chyba nikt nie czyta tego co piszę.
Analiza logu z valgrinda:
Łączna ilośc cykli:
10,654,292,679

Z CZEGO:
main:
8,088,634,294

scanf:
1,250,348,057

memset:
712,649,351

Inne: ~500,000,000

ANALIZA MAIN
Alokacja tablicy:
7,830
uint64_t * safe = new uint64_t[ iloscSkrzatow / 64 + 1 ]();

Serio dynamiczna wielkość jest taka zła?

Pętla główna:
2,552,014
for( int i = 0; i < czasDoSnu; i++ )

Odtąd złożoność jest liniowa dla czasDoSnu.

Tworzenie i falsyfikacja tablic safeT:
2,552,012
memset( safeT, 255, iloscSkrzatow / 8 );

6,380,030
for( int m = 0; m < iloscSkrzatow % 8; m++ )

.
setTrue( safeT, iloscSkrzatow - iloscSkrzatow % 8 + m );

712,649,351
/ build / buildd / eglibc - 2.17 / string /../ sysdeps / i386 / i686 / multiarch / memset

Łącznie prawie 10% programu, ale co poradzić, testy wykazały że jest to najoptymalniejsze rozwiązanie.

Praktycznie cała reszta czasu jest spędzana wewnątrz pętli
for( int m = 0; m < iloscSkrzatow / 64 + 1; m++ )

Od tego momentu złożoność jest teoretycznie proporcjonalna do czasDoSnu*iloscSkrzatow/64
No i nasz najzasobożerniejszy element:
2,995,428,773
if( safe[ m ] & safeT[ m ] )

1,496,114,690
_for( int p = 0; p < iloscSkrzatow / 64 + 1; p++ )

1,994,394,252
_ safe[ p ] |= safeT[ p ];

No i mamy wspomniane ^2.
Ale, ale:
.
break;

Jak łatwo zauważyć ilość cykli pętli
for( int m = 0; m < iloscSkrzatow / 64 + 1; m++ )
 i
for( int p = 0; p < iloscSkrzatow / 64 + 1; p++ )
 są praktycznie równe: ilości cykli to odpowiednio 1,500,585,401 i 1,496,114,690.
Oznacza to, że w praktyce złożoność nie jest kwadratowa, a liniowa.
OPŁACA SIĘ CZYTAĆ TO CO SIĘ KOMENTUJE.
Pisałem też, że TO SAMO rozwiązanie na wektorach było kilkukrotnie wolniejsze...
P-92717
DejaVu
» 2013-09-27 11:22:58
Bo z vectora trzeba umieć korzystać. Wystarczyłoby zrobić:
C/C++
wektor.reserve( iloscSkrzatow / 64 + 1 )
i masz taką samą wydajność jak dynamiczna alokacja tablicy.
P-92718
1 2 « 3 » 4 5
Poprzednia strona Strona 3 z 5 Następna strona