<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>pamiętnik programisty &#187; spoj</title>
	<atom:link href="http://piotr.doniec.eu/devlog/tag/spoj/feed/" rel="self" type="application/rss+xml" />
	<link>http://piotr.doniec.eu/devlog</link>
	<description></description>
	<lastBuildDate>Wed, 28 Dec 2011 23:52:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>HSSPOJ &#8211; zadanie które nie dawało mi spokoju</title>
		<link>http://piotr.doniec.eu/devlog/2010/01/hsspoj-zadanie-ktore-nie-dawalo-mi-spokoju/</link>
		<comments>http://piotr.doniec.eu/devlog/2010/01/hsspoj-zadanie-ktore-nie-dawalo-mi-spokoju/#comments</comments>
		<pubDate>Fri, 01 Jan 2010 19:37:11 +0000</pubDate>
		<dc:creator>pejotr</dc:creator>
				<category><![CDATA[Algorytmika]]></category>
		<category><![CDATA[Programowanie]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[spoj]]></category>
		<category><![CDATA[teoria gier]]></category>
		<category><![CDATA[tricky]]></category>
		<category><![CDATA[twierdzenie Sprague-Grundy'ego]]></category>

		<guid isPermaLink="false">http://piotr.doniec.eu/devlog/?p=381</guid>
		<description><![CDATA[Zadanie z konkursu algorytmicznego dla uczniów szkół średnich. Zwróciłem na nie uwagę, gdyż współczynnik poprawnych rozwiązań wynosił lekko ponad 30%, a skoro to konkurs dla licealistów to powinienem sobie dać radę. I tu pierwszy szok, nic z tego&#8230; Wiedza jaką dysponowałem okazała się daleko nie wystarczająca, a próby znalezienia rozwiązania podanego na tacy spełzły na [...]]]></description>
			<content:encoded><![CDATA[<p>Zadanie z konkursu algorytmicznego dla uczniów szkół średnich. Zwróciłem na nie uwagę, gdyż współczynnik poprawnych rozwiązań wynosił lekko ponad 30%, a skoro to konkurs dla licealistów to powinienem sobie dać radę. I tu pierwszy szok, nic z tego&#8230; Wiedza jaką dysponowałem okazała się daleko nie wystarczająca, a próby znalezienia rozwiązania podanego na tacy spełzły na niczym. Co dodatkowo rozbudziło moją ciekawość to fakt że na forach poświęconych programowaniu, nikt nie szuka pomocy.<br />
Zadanie finalnie okazało się rozbudowaną grą Nim, ale bez znajomości jednego twierdzenia teorii gier prawdopodobnie siedziałbym na tym jeszcze dłuuuugo&#8230;<br />
Treść zadania dostępna pod adresem <a href="http://www.spoj.pl/HSPLARCH/problems/HS09GAME/">http://hs.spoj.pl/problems/HS09GAME/</a><br />
<span id="more-381"></span><br />
Wspomniane twierdzenie to Twierdzenie Sprague-Grundy&#8217;ego. Gra przestawiona w zadaniu spełnia wszystkie założenia pozwalające na skorzystanie z twierdzenia. W połączeniu z dokładnie omówioną optymalną strategią, opisem pozycji wygrywających i przegrywających w grze Nim, pierwszą część zadania można rozwiązać w zaledwie 9 liniach</p>
<pre class="brush: python">
# gry - lista skladajaca sie z list reprezentujacych gre. Kazda reprezentacja
# gry to kolejna lista zaiwerajaca ilosc &#039;kijkow&#039; na kolejnych stosach
def graj(gry):
    sg = (0, 0, 1, 1, 2, 2, 3)
    for gra in gry:
        g = []
        reszty = []
        for stos in gra:
            reszta = int(stos)%7
            g.append(sg[reszta])
            reszty.append(reszta)
        wynik = reduce(lambda x,y: x^y, g)

        if wynik :
            print &quot;Julia wins.&quot;
        else :
            print &quot;Robert wins.\n&quot;
</pre>
<p>Lekki problem pojawia się przy ustalaniu ruchu wygrywającego. Nie wiem czy istnieje jakieś szybsze rozwiązanie niż takie trochę brute-force&#8217;owe która ja wykorzystałem. Polega ono na stwierdzeniu na jakiej pozycji ( wygrywającej czy przegrywającej ) znalazł by się gracz gdyby nie było jednego stosiku, a następnie obliczeniu czy da się odjąć taką liczbę patyczków żeby zostawić przeciwnika na pozycji przegrywającej.<br />
Dodatkowo oprócz wskazania ruchu wygrywającego należy wskazać ten podczas którego bierze się najwięcej patyczków ze stosu o najmniejszym numerze, co w lekki sposób komplikuje sprawę.:</p>
<pre class="brush: python">
        stos_wygrywajacy = 0;
        licznik = 0
        blokada_5 = False
        blokada_3 = False

        # odwrocenie list, sprawdzanie od konca
        gra_rev = gra[:]
        gra_rev.reverse()

        g_rev = g[:]
        g_rev.reverse()

        reszty.reverse()
        for reszta in reszty:
            # lista reszt bez kolejnych stosow
            tmpr = reszty[:]
            tmpr.pop(licznik)

            # xor wartosci f. Sprague-Grundy&#039;ego
            # bez kolejnych stosow
            tmpg = g_rev[:]
            tmpg.pop(licznik)
            tmpw = reduce(lambda x,y: x^y, tmpg ,0)

            if sg[reszta-5] == tmpw and int(gra_rev[licznik]) &gt;= 5:
                wygrywajacy = 5
                stos_wygrywajacy = len(reszty) - licznik
                blokada_5 = True
            elif sg[reszta-3] == tmpw and int(gra_rev[licznik]) &gt;= 3 and blokada_5 == False:
                wygrywajacy = 3
                stos_wygrywajacy = len(reszty) - licznik
                blokada_3 = True
            elif sg[reszta-2] == tmpw and int(gra_rev[licznik]) &gt;= 2 and blokada_5 == False and blokada_3 == False:
                wygrywajacy = 2
                stos_wygrywajacy = len(reszty) - licznik

            licznik += 1
</pre>
<p>Jeśli ktoś zna szybsze rozwiązanie chętnie je przeanalizuję <img src='http://piotr.doniec.eu/devlog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /><br />
Zaznaczam, że nie dam sobie głowy uciąć że algorytm jest poprawny, bo nie ma już możliwości zgłoszenia rozwiązania &#8211; upłynęły terminy.</p>
<p><strong><em>EDIT</em></strong>:<br />
Lekko poprawiona wersja, działa trochę szybciej, natomiast i tak nie umożliwia zdobycia maximum punktów. Jesli nie ma jakiejś sztuczki na ruch wygrywający, a podejrzewam że coś jest, to lepiej zakodzić to w C&#8230;</p>
<pre class="brush: python">
        stos_wygrywajacy = 0
        blokada_3 = False
        blokada_2 = False
        licznik = 0
        for reszta in reszty :
            tmpg = g[:]
            tmpg.pop(licznik)
            tmpw = reduce(lambda x,y: x^y, tmpg, 0)

            if sg[reszta-5] == tmpw and int(gra[licznik]) &gt;= 5:
                wygrywajacy = 5
                stos_wygrywajacy = licznik + 1
                break
            elif sg[reszta-3] == tmpw and int(gra[licznik]) &gt;= 3 and blokada_3 == False:
                wygrywajacy = 3
                stos_wygrywajacy = licznik + 1
                blokada_3 = True
            elif sg[reszta-2] == tmpw and int(gra[licznik]) &gt;= 2 and blokada_3 == False and blokada_2 == False:
                wygrywajacy = 2
                stos_wygrywajacy = licznik + 1
                blokada_2 = True
            licznik += 1
</pre>
]]></content:encoded>
			<wfw:commentRss>http://piotr.doniec.eu/devlog/2010/01/hsspoj-zadanie-ktore-nie-dawalo-mi-spokoju/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>

