Documente Academic
Documente Profesional
Documente Cultură
Jocul NIM
Introducere
Domeniu relativ nou şi încă necercetat în adâncime, teoria jocurilor este o
ramură a matematicii în care de multe ori primează inventivitatea şi nu
cunoştinţele. Tocmai din acest motiv, în cadrul acestui articol vom introduce
câteva noţiuni teoretice care ne vor ajuta în rezolvarea unor probleme din teoria
jocurilor. Ingeniozitatea celor pasionaţi poate fi testată prin introducerea unor
probleme de teoria jocurilor la concursurile de matematică şi informatică.
Deoarece în România teoria jocurilor nu este studiată în şcoli, problemele din
acest domeniu pot pune în dificultate concurenţii.
Jocuri combinatoriale
Definiţia 1 Prin joc se înţelege un şir de decizii (acţiuni, mutări), luate de părţi
ale căror interese sunt contrare.
Jocurile pot fi clasificate după numărul de jucători, după natura mutărilor
(libere sau aleatoare, cum ar fi zaruri, cărţi de joc, etc), după cantitatea de
informaţie disponibilă (cunoaştem sau nu informaţii despre toate mutările
precedente?).
Jocurile despre care discutăm în continuare sunt jocuri pentru 2 jucători,
care mută alternativ, mutările sunt libere (nu există nimic aleator) şi informaţia
este completă (cunoaştem toate mutările efectuate).
Soluţie
Lectia 5. Strategii de joc. Jocul NIM
Initial X va haşura căsuţele din centrul benzii, împărţind în acest fel banda de
hârtie în două benzi de dimensiuni egale, putând în acest fel să imite simetric
mutările lui Y.
Exemplul 2
Fie o tablă dreptunghiulară împărţită în n*m căsuţe. Alternativ, doi jucători X şi
Y haşurează câte două căsuţe adiacente, nehaşurate până în acel moment. Cel
care nu mai poate muta pierde. Iniţial mută jucătorul Y. Cel puţin unul dintre
numerele m, n este un număr par. Se cere:
a) Să se determine dacă jucătorul X are strategie sigură de câştig.
b) În caz afirmativ se cere să se programeze mutările lui X, cele ale lui Y fiind
citite de la tastatură.
Soluţie
Vom căuta o axă de simetrie (pentru că cel puţin unul dintre m şi n este par). Să
alegem ca axa de simetrie linia care este paralelă cu marginea tablei care are un
număr impar de pătrăţele (dacă aceasta există) şi este egal distanţată de cele
două margini. Dacă Y joacă de o parte a axei de simetrie, X va juca în cealaltă
parte, dar dacă Y va haşura două pătrăţele, unul de o parte a axei, iar celălalt de
cealaltă parte, atunci X nu mai are cum să îl imite simetric, de aceea este
evidentă necesitatea unei noi axe de simetrie, lucru care este posibil, doar daca
ambele numere n, m sunt pare. Dacă doar unul din numerele n, m este par, atunci
Y, va putea hasura primele doua patratele pe linia (coloana) din centru, avand o
patratica de o parte a axei, iar cealalta patratica de cealalta parte a axei, iar apoi
va imita simetric mutarile lui X.
In concluzie, X are strategie sigura de castig, daca ambele numere n, m sunt
pare, iar o poziţie (p,q), va avea simetrica de coordonate (n+1-p,m+1-q).
Exemplul 1
Se dă o foaie de hârtie dreptunghiulara, împărţită în n*m căsuţe. Alternativ, doi
jucători X şi Y decupează una dintre căsuţele care este învecinată pe orizontală,
sau verticală cu ultima căsuţă decupată. Cel care nu mai poate muta pierde.
Iniţial mută X şi va decupa o căsuţă oarecare. O căsuţă poate fi decupată o
singură dată, deci nu este permisă parcurgerea de două ori a aceleiaşi căsuţe. Se
cere:
a) Să se determine daca X are strategie sigură de câştig.
Lectia 5. Strategii de joc. Jocul NIM
b) În caz afirmativ să se programeze mutarile lui X, cele ale lui Y fiind citite de la
intrare
Solutie
Ideea de bază este gruparea mutărilor în perechi. În acest caz încercăm să
acoperim tabla cu piese de domino. Dacă tabla are un număr impar de căsuţe,
atunci nu este posibilă o acoperire completa a acesteia, dar in schimb o putem
acoperi, daca eliminam, spre exemplu coltul din stanga sus, deoarece pe linia 1,
vom ramane cu un numar par de casute, pe care le putem acoperi complet, fără
să suprapunem piese, iar în rest mai ramane o suprafaţă dreptunghiulara care are
un numar par de casute, care pot fi acoperite cu dominouri.
Daca tabla are un numar par de casute, atunci cel de-al doilea jucator (Y) are
strategie sigura de câştig, deoarece la fiecare pas, va juca în acea căsuţa, care se
afla sub acelaşi domino cu casuta decupata de X. Daca tabla are un numar impar
de casute, atunci jucatorul care muta primul (X) are strategie sigură de câştig,
iniţial el decupand casuta care nu a fost acoperita de nici un Domino, apoi la
fiecare pas decupand casuta pereche casutei decupate de Y.
Exemplul 2
Se da o foaie de hârtie dreptunghiulară, împărţită în n*n căsuţe. Alternativ, doi
jucatori X şi Y decupeaza una din casutele care este L-invecinata cu ultima
casuta decupata. Două casute se numesc L-învecinate, dacă există o săritură a
calului care să treacă de la una la alta (dintr-o singură mutare). Cel care nu mai
poate muta pierde. O casuta poate fi decupata o singura data, deci nu este
permisa parcurgerea de doua ori a aceleiaşi căsuţe. Se cere:
a) Sa se determine daca X are strategie sigura de castig.
b) In caz afirmativ sa se programeze mutarile lui X, cele ale lui Y fiind citite de
la intrare.
Soluție
O mutare este identica cu o saritura a calului, intr-o pozitie noua,
neparcursa. Trebuie sa gasim o impartire a casutelor tablei in perechi, astfel
incat fiecare casuta sa faca parte dintr-o singura pereche. Sa definim o pereche
de casute, ca fiind doua casute astfel incat un cal poate sa sara direct de pe una
pe alta. Deci daca reusim sa gasim un drum al calului, care sa treaca o singura
data prin fiecare casuta, atunci problema este rezolvata, deoarece casuta cu
numar de ordine impar in cadrul drumului, va fi prima casuta a Domino-ului in
forma de L, definit anterior, iar casutele cu numar de ordine par vor fi cea de-a
doua casuta a Domino-ului.
Pentru n par, jucatorul Y are strategie sigura de castig, la fiecare pas el
jucand in casuta pereche celei in care a jucat X.
Pentru n impar, jucatorul X are strategie sigura de castig, initial el
decupand pozitia din centru, apoi la fiecare pas va juca mutarea pereche a
mutarii executate de Y.
Determinarea unui drum al calului care sa treaca o singura data prin
fiecare casuta, se face poate prin urmatorul procedeu:
- Se pleaca din pozitia (1,1). Se poate pleca la fel de bine din orice alta pozitie.
Lectia 5. Strategii de joc. Jocul NIM
- La fiecare pas se sare in acea casuta din care sunt cele mai putine posibilitati
de a sari, mai putin in casuta din centru.
Exemplul 1
Se dă o stivă cu n monede. Alternativ, doi jucători X şi Y pot extrage din
stivă maximum k monede. Cel care nu mai poate muta, pierde. Iniţial
mută X. Se cere:
a) Să se determine dacă X are strategie sigură de câştig.
b) În caz afirmativ să se programeze mutările lui X, cele ale lui Y fiind
citite de la intrare. (Jocul Bachet, 1612)
Soluţie
Strategia câştigătoare se bazează pe împărţirea configuraţiilor de joc în două
mulţimi disjuncte, şi anume:
- configuraţia pară se defineşte ca fiind acel n, cu proprietatea ca n%(k+1)==0
- configuraţia impară se defineşte ca fiind acel n, cu proprietatea ca n%(k+1)!=0
Teorema 1
Lectia 5. Strategii de joc. Jocul NIM
Demonstraţie
Configuraţia pară este caracterizată de faptul ca n este divizibil cu k+1. La
fiecare pas putem extrage p monede, p cuprins intre 1 si k. Deci în noua
configuraţie n nu va fi divizibil cu k+1.
Teorema 2
Dintr-o configuraţie impară, se poate trece, în urma unei mutări convenabile,
într-o configuraţie pară.
Demonstraţie
Să presupunem că în stivă avem n monede. Cea mai apropiată configurație pară
(cu mai puţine monede decât n) se obţine extrăgând n%(k+1) monede.
Deci jucătorul X fiind primul la mutare, va câştiga (mutând corect, fără
greşeli) dacă n%(k+1)!=0.
Exemplul 2
Se dă o stivă cu n monede. Dacă numărul monedelor din stivă, este mai
mare decât un număr natural dat p, atunci din stivă se pot extrage maximum k1
monede, în caz contrar se pot extrage maximum k2 monede. Alternativ, doi
jucători X şi Y, extrag din stivă monede, respectând condiţia de mai sus. Inițial
mută X. Se cere:
a) Să se determine dacă X are strategie sigură de câştig.
b) În caz afirmativ să se programeze mutările lui X, cele ale lui Y fiind citite de
la intrare.
Soluţie
Să determinam cel mai mare număr natural w, mai mic sau egal cu p, astfel
încât w%(k2+1)==0. Deci cel care va ajunge să ia primul din stiva cu w
monede, va pierde. Deci pentru ca Y să fie obligat să extragă dintr-o stivă cu w
monede, X va trebui să mute în aşa fel încât să lase în stivă w monede. Asta
înseamnă că X trebuie să câştige pentru o stivă de n-w monede, din care se pot
extrage la fiecare pas, maxim k1 monede, lucru realizabil doar dacă n-w este
multiplu de k1+1.
Exemplul 3.
Jocul NIM
Se dau n grămezi, fiecare conţinând un anumit număr de pietre. Doi jucători vor
începe să ia alternativ din pietre, astfel: la fiecare pas, jucătorul aflat la mutare
trebuie să îndepărteze un număr nenul de pietre dintr-o singură grămadă.
Câştigătorul este cel care ia ultima piatră.
Cerinţă
Lectia 5. Strategii de joc. Jocul NIM
1 ≤ ni ≤ 10 000
Numărul de pietre din oricare grămadă este natural pozitiv mai mic sau egal
cu 2*109
Exemplu
nim.in nim.out
2 NU
4 DA
1 3 5 7
3
4 8 17
Soluţie
Pentru cazul trivial în care numãrul de grãmezi este egal cu 1, primul jucator are
evident strategie de câştig, el putând lua toate pietrele din grãmadã.
Dacã numãrul de grãmezi este egal cu 2, primul jucãtor are strategie de câştig
atunci când numãrul de pietre din prima grãmadã este diferit de numãrul de
pietre din cea de-a doua, strategia lui fiind cea de a aduce tot timpul grãmada
mai mare la numãrul de pietre al grãmezii mai mici, şi cum jocul este finit,
înseamnã cã primul jucãtor o sã aducã jocul în starea (0, 0).
Dacã numãrul de grãmezi este mai mare decât doi strategia se complicã şi nu se
mai observã cu "ochiul liber". Dar ideea este similară.
O strategie posibilă ar fi următoarea:
Considerăm reprezentările binare ale numărului de pietre din fiecare grămadă.
Efectuăm operaţia XOR (sau exclusiv ^) pe aceste reprezentări.
Dacă rezultatul acestei operaţii este diferit de 0, primul jucător va avea
strategie sigură de câştig.
Dacă rezultatul acestei operaţii este 0, al II-lea jucător va avea strategie
sigură de câştig (deci primul jucător este într-o stare de pierdere).
Să demonstrăm că primul jucător are strategie sigură de câştig dacă
rez=p1^p2^…pn≠0.
Ideea este de a muta astfel încât să aducă pe al doilea jucător într-o stare de
pierdere, adică pentru care rez=p1^p2^…pn=0.
Lectia 5. Strategii de joc. Jocul NIM
Fie i cel mai semnificativ bit egal cu 1 din rez. Alegem o grămadă j care
are în reprezentarea sa binară acest bit egal cu 1.
Voi extrage din această grămadă pj-(pj^rez) pietre (pj^rez este mai
mic decât pj, pentru că bitul i se anulează) După această mutare p1^p2^…
pn=0, iar al doilea jucător va trebui să extragă un număr de pietre, ceea ce va
conduce iarăşi la o stare pentru care p1^p2^…pn≠0 (adică primul jucător va
avea ce muta).
Dacă suntem într-o stare cu rez=0, orice mutare ar face jucătorul care este la
rând va aduce jocul într-o stare cu rez≠0 (să spunem că din gămada j va luam
x piese; bitul cel mai semnificativ din x se schimbă sigur)
De exemplu, dacã avem o gramadã cu o piatrã, o gramadã cu trei pietre, o
gramadã cu cinci pietre şi o gramadã cu şapte pietre:
o
ooo
ooooo
oooooo
Atunci vom avea:
1 = (0001)
3 = (0011)
5 = (0101)
7 = (0111)
rez=(0000)
Conform propoziţiei de mai sus aceastã stare este de pierdere.
Să considerăm un alt exemplu
N=3 p=(4,8,17)
Reprezentările binare sunt
p1: 00100
p2: 01000
p3: 10001
rez:11101
Cel mai semnificativ bit 1 din rez corespunde lui 24
Aleg o grămada j pentru care pj are bitul corespunzător lui 24 egal cu 1 (aceasta
trebuie să existe şi este p3)
Extrag din grămada 3: p3-(rez^p3) piese, adică în grămada p3 rămân p3^rez
piese
rez: 11101
p3: 10001
rez^p3: 01100=12 piese
Extrag p3-(rez^p3)=17-12=5 piese din grămada 3 şi se obţine p3=12:
p1: 00100
p2: 01000
p3: 01100
rez:00000
Orice mutare ar face al doilea jucător, ca determina ca rez să devină iarăşi ≠0.
Lectia 5. Strategii de joc. Jocul NIM
#include <bits/stdc++.h>
using namespace std;
ifstream fin("nim.in");
ofstream fout("nim.out");
int main()
{
int T,x;;
fin>>T;
for (int t = 0; t < T; t++)
{
int n;
fin>>n;
int val = 0;
for (int i = 1; i <= n; i++)
{
fin>>x;
val ^= x;
}
if (val != 0)fout<<"DA\n";
else fout<<"NU\n";
}
return 0;
}
Probleme similare
Problema 1
Pe o tablã de şah, care are n • m cãsuţe, sunt plasaţi pe prima linie n pioni albi şi
pe ultima linie n pioni negri. Fiecare dintre cei doi jucãtori poate muta un singur
pion, care îi aparţine, un numãr strict pozitiv de cãsuţe în sus sau în jos, astfel
încât sã nu ajungã vreun pion alb sã fie mai jos decât pionul negru de pe aceeaşi
coloanã. Pierde jucãtorul care nu mai poate muta.
Soluţie
Aceastã problemã este o deghizare a jocului NIM, numãrul de pãtrãţele libere
între pionul alb şi pionul negru de pe coloana i putând fi considerat numãrul de
pietre din grãmada i. Singura diferenţã este cã se pot adãuga pietre la grãmadã
(existând posibilitatea mutãrii înapoi).
Aceastã problemã se rezolvã uşor, jucãtorul care are strategie de câştig putând
evita asemenea mutãri. O astfel de mutare poate fi utilã numai pentru jucãtorul
care este într-o poziţie de pierdere.
Când jucatorul care nu are strategie de câştig mutã înapoi x casuţe, celãlalt
jucãtor va muta pionul propriu de pe aceeaşi coloanã cu x cãsuţe în faţã, astfel
ajungându-se la aceeaşi stare cu cea existentã cu douã mutãri anterior
(considerând diferenţa poziţiilor).
Problema 2
Această problemã a fost propusã spre rezolvare participanţilor la barajul pentru
selecţia lotului naţional din 1997.
Pe o linie sunt plasate la coordonate întregi 2 • n piese roşii şi albastre.
Fiecare piesã roşie poate fi mutatã în dreapta oricâte poziţii astfel încât sã nu
sarã peste o piesã albastrã, iar piesele albastre pot fi mutate oricâte poziţii la
Lectia 5. Strategii de joc. Jocul NIM
stânga astfel încât sã nu depăşeascã vreo piesă roşie. Piesele vor alterna: roşu,
albastru, roşu, albastru etc. Pierde jucãtorul care nu mai poate muta.
Soluţie
Aceastã problemã poate fi, de asemenea, redusã la jocul NIM. Diferenţele
poziţiilor perechilor de piese roşii şi albastre consecutive constituie numãrul de
pietre al grãmezilor din jocul NIM.
2. Apoi, programul vostru va determina daca primul jucator are sau nu strategie
sigura de castig. In caz afirmativ, va scrie o linie ce contine doar valoarea 1. In
caz contrar (deci daca primul jucator nu are strategie sigura de castig) se va
scrie o linie ce contine doar valoarea 0 (zero) si apoi o linie linie care contine
cuvantul DONE.
3. Daca primul jucator are strategie sigura de castig, programul vostru va
efectua un numar de runde. O runda consta din scrierea unei linii la iesirea
standard, care descrie operatia efectuata de primul jucator în runda respectiva. O
operatie este descrisa prin numarul gramezii din care jucatorul extrage pietre (1
pentru prima gramada, 2 pentru cea de a doua, respectiv 3 pentru ambele
gramezi), urmat de un spatiu, apoi de un numar natural nenul care reprezinta
numarul de pietre extrase. Apoi, se va citi o linie de intrarea standard care
descrie operatia efectuata de al doilea jucator (operatia fiind specificata in
acelasi mod). Când programul vostru a executat ultima mutare, afiseaza o linie
speciala, continand numai cuvantul DONE
Punctaj
Programul vostru va primi punctajul corespunzator setului de teste curent daca
si numai daca a rezolvat corect toate testele continute in setul respectiv.
Programul vostru primeste 0 puncte pe un anumit test in urmatoarele situatii : -
afiseaza in cadrul unei runde o operatie ce nu poate fi executata la momentul
respectiv; - afiseaza ca primul jucator are strategie sigura de castig atunci cand
nu are (sau invers); - atunci cand primul jucator are strategie sigura de castig,
programul vostru nu castiga; - formatul de afisare impus in problema nu este
respectat.
Instructiuni de programare
Programatorii în C/C++ trebuie sa execute flush dupa ce au terminat de scris o
linie completa la iesire. Acest lucru este valabil si pentru linia continând DONE
În cazul programelor scrise în C++ si utilizarea iostreams, se va folosi
urmatoarea secventa pentru citirea de la intrarea standard si scrierea la iesirea
standard: cin>>x; cout<<op<<'\n'<<flush;
În cazul programelor în C sau C++ si utilizarea scanf si printf, se va folosi
urmatoarea secventa pentru citirea de la intrarea standard si scrierea la iesirea
standard: scanf ("%d", &x); printf("%d\n",op); fflush (stdout);
În cazul programelor în Pascal se va folosi urmatoarea secventa pentru citire de
la intrarea standard se utilizeaza readln, iar pentru afisarea unei linii se
utilizeaza writeln : readln(x); writeln(op);
Restrictii si precizari
1 <= n <= 1016 Intr-un set de teste sunt cel mult 7 teste.
Exemplu de interactiune
Operatie Semnificatie
Citeste 1 2 n=1, m=2 Primul jucator nu are strategie
Scrie 0 sigura de castig S-a terminat primul test
Scrie DONE n=5, m=6
Citeste 5 6
Scrie 1 Primul jucator are strategie sigura de castig
Lectia 5. Strategii de joc. Jocul NIM
Soluţie
Rezolvarea se bazează pe reprezentarea numerelor naturale în sistemul de
numeraţie Fibonacci.
Definiţie
Perechea (n, m), cu n<m se numeşte distinsă dacă:
1. reprezentarea Fibonacci a lui n se termină cu un număr par de zerouri;
2. reprezentarea Fibonacci a lui m se obţine din reprezentarea Fibonacci a lui n
prin adăugarea la sfârşit a unui 0.
Exemple:
1. n = 1 (1, în sistemul Fibonacci) , m = 2 (10, în sistemul Fibonacci)
2. n = 3 (100, în sistemul Fibonacci), m = 5 (1000, în sistemul Fibonacci)
3. n = 4 (101, în sistemul Fibonacci), m = 7 (1010, în sistemul Fibonacci)
Propoziţie
Orice număr natural aparţine unei singure perechi distinse.
Demonstraţie
Dacă reprezentarea numărului în sistemul de numeraţie Fibonacci se termină cu
un număr par de zerouri, al doilea număr din pereche se obţine adăugând un 0
final, altfel al doilea număr din pereche se obţine prin suprimarea unui 0 final.
Propoziţie
În şirul {m - n | (n, m) pereche distinsă} fiecare număr natural apare o singură
dată.
Demonstraţie
Fie d ∈ N, iar xpxp - 1...x1, reprezentarea Fibonacci a lui d.
d = xpxp - 1...x1 = xp∙ fp + xp - 1∙ fp-1+...+x1f1
Dacă reprezentarea Fibonacci a lui d se termină cu un număr impar de zerouri,
atunci
n = xpxp - 1...x10 = xp∙ fp + 1 + xp - 1∙ fp+...+x1∙ f2 (5)
m = xpxp - 1...x100 = xp∙ fp + 2 + xp - 1∙ fp + 1+...+x1∙ f3. (6)
m - n = xp∙ (fp+2 - fp +1)+ xp - 1∙ (fp + 1-fp)+...+x1∙ (f3 - f2)= xp∙ fp + xp - 1∙ fp-1+...+x1f1 = d
Dacă reprezentarea Fibonacci a lui d se termină cu un număr par de zerouri,
deci x1 = x2 = x2k = 0, x2k+1 ≠ 0, atunci:
n = xpxp - 1...x2k+2 0101...01(de k+1 ori )= xp∙ fp+1 + xp - 1∙ fp+...+x2k+2 ∙ f2k+3 + (f2k +
1 + f2k - 1+...+ f3 + f1)
Lectia 5. Strategii de joc. Jocul NIM
Teoremă
Dacă n şi m, numărul de pietre din prima, respectiv cea de a doua grămadă nu
formează o pereche distinsă, atunci primul jucător are strategie sigură de câştig
(câştigă dintr-o singură mutare, sau poate aduce jocul printr-o singură mutare
într-o situaţie în care (n, m) formează o pereche distinsă.
Schiţa de demonstraţie
1. Dacă n = m sau n = 0 primul jucător câştigă dintr-o singură mutare.
2. Dacă 0 < n < m, determinăm perechea distinsă (n, p) şi perechea distinsă (a,
b) astfel încât b - a = m - n.
Dacă p < m, atunci se poate trece cu o singură mutare de la perechea (n, m) la
perechea distinsă (n, p).
Dacă p > m, deducem p - n > b - a = m - n, deci perechea distinsă (n, p) este
"mai mare" decât perechea distinsă (a, b), deci p > m, a > n. Deducem n - a = m
- b, deci dintr-o singură mişcare putem trece la perechea distinsă (a, b).
3. Dacă perechea (n, m) este distinsă, după orice mişcare ea va înceta să mai fie
distinsă.
grămezi sunt acele stări pentru care suma XOR a numerelor de pietre din
grămezi este diferită de 0, restul stărilor fiind de pierdere.
De exemplu, dacă avem 4 grămezi cu 1, 3, 5, respectiv 7 pietre:
O
OOO
OOOOO
OOOOOOO
1 = (0001)2
3 = (0011)2
5 = (0101)2
7 = (0111)2
Efectuând XOR (operatorul ^ în C/C++) între reprezentările binare ale numerelor,
obţinem 0 = (0000)2. Conform propoziţiei de mai sus, această stare este de pierdere.
Să demonstrăm cele afirmate. Dintr-o poziţie cu suma XOR egală cu 0, pentru orice
mutare ajungem evident la o poziţie cu suma XOR diferită de 0, pentru că luând dintr-o
grămadă un număr x de pietre, suma XOR corespunzătoare noii stări, pe poziţia bitului
de 1 cel mai din dreapta în reprezentarea lui x, va avea valoarea 1.
Mai rămâne de demonstrat că din orice poziţie cu suma XOR diferită de 0 putem trece
printr-o mutare convenabilă într-o poziție cu suma XOR egală cu 0. Căutăm o grămadă al
cărui număr de pietre are bitul 1 pe poziţia celei mai mari puteri a lui 2 care apare în
suma XOR. Fie x valoarea sumei XOR a tuturor grămezilor şi y numărul de pietre din
grămada găsită mai devreme.
O mutare "câştigătoare" este extragerea din grămada găsită care are y pietre a y -
(x XOR y) pietre (x XOR y este mai mic decât y pentru că în y se anulează bitul cel mai
semnificativ al lui x). Atunci noua sumă XOR va fi egală cu 0.
De exemplu:
Mutarea câştigătoare constă în a lua din cea de-a treia gramadă un număr de pietre egal
cu:
Soluţie
Lectia 5. Strategii de joc. Jocul NIM
Această problemă este o deghizare a jocului NIM, numărul de pătrăţele libere între pionul
alb şi pionul negru de pe coloana i putând fi considerat numărul de pietre din
grămada i. Singura diferenţă este că se pot adăuga pietre la grămadă (existând
posibilitatea mutării înapoi). Această problemă se rezolvă uşor, jucătorul care are
strategie de câştig putând evita asemenea mutări. O astfel de mutare poate fi utilă
numai pentru jucătorul care este într-o poziţie de pierdere. Când jucătorul care nu are
strategie de câştig mută înapoi x căsuţe, celălalt jucător va muta pionul propriu de pe
aceeaşi coloanã cu x căsuţe în faţă, astfel ajungându-se la aceeaşi stare cu cea existentă
cu două mutări anterior (considerând diferenţa poziţiilor).
Soluţie
Această problemă poate fi, de asemenea, redusă la jocul NIM. Diferenţele poziţiilor
perechilor de piese roşii şi albastre consecutive constituie numărul de pietre al
grămezilor din jocul NIM.
Soluţie
Strategia acestui joc este similară cu cea aplicată în jocul NIM cu câteva mici diferenţe.
Jucătorul care are strategie de câştig în poziţia curentă în cadrul jocului NIM face aceeaşi
mutare pe care ar face-o în cazul jocului NIM, exceptând cazul în care această mutare
lasă doar grămezi cu o singură piatră şi numărul acestor grămezi este par. În această
situaţie, dacă ar trebui să distrugă o grămadă de x pietre, jucătorul poate lua x - 1, iar
dacă ar trebui să lase o singură piatră din grămada actuală, poate lua întreaga grămadă.
Astfel, numărul de grămezi rămase va fi impar şi el nu va face ultima mutare.
Numerele Sprague-Grundy
Jocurile care prezintă interes pentru jucători sunt acelea care necesită
examinarea unui număr foarte mare de stări pentru determinarea strategiei de câştig,
deoarece în caz contrar câştigătorul s-ar cunoaşte chiar de la început. Spre deosebire de
aceştia, matematicienii sau informaticienii sunt interesaţi de determinarea unor strategii
pentru astfel de jocuri. Toate jocurile imparţiale cu doi jucători cu informaţie totală pot fi
reduse la jocul NIM care se joacă cu nişte grămezi virtuale, în care mutările posibile sunt
extragerea oricâtor pietre dintr-o grămadă sau adăugarea oricâtor pietre la o grămadă
(aşa cum am menţionat anterior, adăugarea de pietre la o grămadă nu complică analiza
jocului).
Afirmaţia anterioară constituie un rezultat al Teoriei Sprague-Grundy. Roland Percival
Sprague (1936) şi Patrick Michael Grundy (1939) sunt doi matematicieni care s-au
ocupat, independent, de teoria jocurilor imparţiale.
Se consideră un graf aciclic care conţine în noduri câţiva pioni, jucătorii alternează la
mutare şi fiecare poate muta câte un pion pe un arc care iese din nodul în care este
situat pionul. Pierde jucătorul care nu mai poate muta.
Lectia 5. Strategii de joc. Jocul NIM
Întrucât graful este aciclic, jocul este finit şi are întotdeauna un câştigător.
Practic, acest joc este suma unor jocuri, mai precis suma a mai multor jocuri cu un
singur pion pe graful aciclic.
Jocul cu un singur pion poate fi analizat destul de uşor, fiecare nod al grafului
putând fi colorat cu alb sau negru după cum există sau nu strategie de câştig dacă în
nodul curent ar fi poziţionat pionul. Această colorare poate fi realizată uşor dacă se
porneşte de la nodurile fără urmaş şi la fiecare pas se colorează câte un nod ai cărui
urmaşi sunt deja coloraţi. Orice joc imparţial poate fi redus la un joc cu un singur pion.
Nodurile sunt poziţiile jocului şi arcele grafului sunt mutările posibile din fiecare poziţie.
Jocul iniţial poate fi şi el transformat, dar numărul de noduri creşte foarte mult
(pentru N pioni şi M noduri, numărul de noduri din jocul transformat este NM) şi nu este
practic să colorăm graful rezultat. Folosind teoria dezvoltată de Sprague şi Grundy,
putem reduce complexitatea analizei jocului cu N pioni la complexitatea analizei jocului
cu un pion.
Vom introduce funcţia mex cu semnificaţia: mex(S) este elementul minimal
natural care nu aparţine mulţimii S. Pentru fiecare nod x al grafului aciclic considerat,
vom calcula valoarea funcţiei gx = mex(gx1, gx2, …, gxk), unde x1, x2, …, xk sunt
urmaşii nodului x în graf. Pentru nodurile fără urmaşi gx = 0.
Analog jocului cu un singur pion, graful poate fi etichetat din aproape în aproape,
pornind de la nodurile fără urmaşi. Observăm că, dacă gx = 0, în jocul cu un singur pion
situat în nodul x nu avem strategie de câştig, iar dacă gx este diferit de 0 avem strategie
de câştig. Restul informaţiei ne ajută la determinarea unei strategii pentru jocul
cu N pioni. Observăm că putem reduce acest joc la jocul NIM în care grămada virtuală
asociată pionului i are un număr de pietre egal cu valoarea g a nodului în care este
situat pionul i.
De ce este acest joc echivalent cu jocul NIM?
Aşa cum la NIM puteam lua oricâte pietre dintr-o grămadă, aici putem muta pionul dintr-
un nod cu valoarea g într-un nod astfel încât noua valoare pentru pionul i poate fi orice
număr de la 0 la g - 1. Prin urmare, pentru a verifica dacă suntem într-o poziţie de
câştig în jocul cu pionii, putem aplica strategia jocului NIM şi obţinem că suntem într-o
poziţie de câştig dacă suma XOR a numerelor din nodurile ocupate de cei N pioni este
diferită de 0.
Problema se rezolvă acum uşor efectuând o sortare topologică a nodurilor grafului aciclic
şi numerotând nodurile folosind funcţia mex.
Soluţie
Să determinăm valorile Sprague-Grundy pentru grămezi mici:
0 : 0; 1 : 1; 2 : 2; 3 : 0; 4 : 1; 5 : 2; 6 : 0
Observăm şi aici secvenţa repetitivă 0 1 2 0 1 2, deci am putea trage concluzia că
valoarea Sprague-Grundy asociată unei grămezi de dimensiune n este n modulo 3.
Această afirmaţie este adevarată şi urmează aceeaşi demonstraţie ca în cazul problemei
anterioare, iar restul modulo 3 pentru un număr cu 200 de cifre este simplu de găsit
determinând suma cifrelor numărului.
Soluţie
Numărul 100000 nu este foarte mare şi valorile Sprague-Grundy pot fi determinate off-
line şi incluse în programul nostru sub formă de constante. Putem scrie pentru valori mici
secvenţa Sprague-Grundy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
0 1 0 2 1 3 0 4 2 5 1 6 3 7
Pentru n impar valoarea asociată este aceeaşi cu valoarea asociată lui n/2, şi
pentru n par valoarea asociată este n/2. Acest lucru se poate demonstra prin inducţie
matematică.
Soluţie
Problema generală are o soluţie ingenioasă care ţine seama de parităţile
rândurilor, dar la această problemă, datorită mărginirii lui Si (Si ≤ 10) nu este necesar
să fim ingenioşi. Restricţia Si ≤ 10 ne ajută prin faptul cã numărul total de poziţii (dacă
jucăm pe o singură gramadă), este 210. Vom reprezenta o poziţie printr-un întreg, iar
dacă acel întreg are în codificarea lui binară pe poziţia i un bit de 1 înseamnă că el
reprezintă un rând de beţe care conţine în el băţul numerotat cu i. Este uşor de realizat
un graf aciclic al mişcărilor pentru un rând (graful este aciclic pentru că la fiecare mutare
luăm beţe din configuraţie). Numerotăm fiecare poziţie cu numerele Sprague-Grundy, şi
acum problema deciderii dacă suntem sau nu într-o poziţie câştigătoare devine banală.
Lectia 5. Strategii de joc. Jocul NIM
sticks.h
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
#define REP( i, n ) \
for ( int i = 0; i < (n); i++ )
#define ALL( c ) (c).begin(), (c).end()
namespace sticks {
static const int
MAXN = 10,
MAXVAL = 10;
int SG[1 << MAXVAL];
vector< int > V;
void calc_SG() {
/* Sprague-Grundy */
SG[0] = 0; // P-position
REP( i, (1 << MAXVAL) ) {
if ( !i ) continue;
vector< int > nxt;
REP( j, 3 )
REP( k, MAXVAL ) {
int mask = ((1 << (j+1)) - 1) << k;
if ( (i & mask) == mask )
nxt.push_back( SG[i - mask] );
}
sort( ALL( nxt ) );
nxt.erase( unique( ALL( nxt ) ), nxt.end() );
int ans = 0;
while ( ans < nxt.size() && nxt[ans] == ans )
ans++;
SG[i] = ans;
}
}
} else {
REP( i, V.size() )
if ( V[i] > 0 ) {
pile = i;
lo = hi = __builtin_ctz( V[i] );
}
}
// 1-based
pile++; lo++; hi++;
printf( "Machine remove [%d,%d] from pile %d\n", lo, hi,
pile );
};
using sticks::getPiles;
using sticks::putMove;
using sticks::getMove;
Solutie:
#include "sticks.h"
#include <cstdio>
#include <algorithm>
#include <vector>
#define REP( i, n ) \
for ( int i = 0; i < (n); i++ )
const int
MAXN = 10,
MAXVAL = 10;
int N;
int player;
vector< int > V;
int SG[1 << MAXVAL];
int main() {
/* Sprague-Grundy */
SG[0] = 0;
REP( i, (1 << MAXVAL) ) {
if ( !i ) continue;
int sgval = 0;
REP( i, V.size() )
sgval ^= SG[ V[i] ];
if ( sgval ) {
int leftcol = 31 - __builtin_clz( sgval );
REP( i, V.size() )
if ( SG[ V[i] ] & (1 << leftcol) ) {
REP( j, 3 )
REP( k, MAXVAL ) {
int mask = ((1 << (j+1)) - 1) << k;
if ( (V[i] & mask) == mask )
{
int sgnxt = (sgval ^ SG[ V[i] ]) ^ SG[ V[i] - mask ];
if ( sgnxt == 0 ) {
lo = k;
hi = k + j;
pile = i;
goto finish;
}
}
}
finish:;
break;
}
} else {
REP( i, V.size() )
if ( V[i] != 0 ) {
lo = hi = __builtin_ctz( V[i] );
pile = i;
break;
}
}
putMove( pile + 1, lo + 1, hi + 1 );
}
int mask = (1 << (hi - lo + 1)) - 1;
mask <<= lo;
assert( (V[pile] & mask) == mask );
V[pile] -= mask;
player ^= 1;
}
return 0;
}
Doi participanţi mănâncă alternant din nişte tablete de ciocolată după următoarele
reguli:
• taie o tabletă în două, tăietura trebuie să fie paralelă cu una din laturile tabletei şi
trebuie să nu taie pătrăţelele de ciocolată;
• pot să rupă şi să mănânce orice linie sau coloană de pătrăţele care nu se află pe
marginea tabletei;
• pot să rupă şi să mănânce toate patrăţelele de pe marginea tabletei, cu condiţia ca
tableta rămasă să aibă cel puţin dimensiunea 1 × 1.
Lectia 5. Strategii de joc. Jocul NIM
Niciuna dintre aceste trei mutări nu poate fi efectuată asupra unei tablete de
dimensiune 1 × 1. Pierde jucătorul care nu mai poate efectua nicio mutare. În fişierul de
intrare se va afla numărul N (1 ≤ N ≤ 100) de tablete, iar pe următoarea linie vor
fi N perechi de numere întregi care reprezintă dimensiunile tabletelor. În fişierul de ieşire
se va afla un singur număr întreg reprezentând numărul mutărilor câştigătoare pentru
primul jucător.
Soluţie
Pentru această problemă vom calcula matricea SGi,j care reprezintă valoarea Sprague-
Grundy asociată unei tablete de dimensiuni (i, j). Să vedem care este recurenţa care
va satisface elementele matricei SG:
Acum, pentru a calcula numărul de mutări câştigătoare efectuăm asupra fiecărei tablete
din fişierul de intrare toate mutările posibile (care sunt cel mult 4 * 100 + 1) şi facem
suma XOR a valorilor Sprague-Grundy pentru restul tabletelor neimplicate în mutare şi a
tabletelor rezultate din mutare. Pentru a calcula SGi,j trebuie sã parcurgem cel mult 2 *
i + 2 * j + 1 valori obţinute. Astfel, algoritmul de determinare a valorilor
matricei SG are ordinul de complexitate O(N3). Complexitatea algoritmului care determină
numărul de mutări câştigătoare este O(N2).
Probleme suplimentare
Joc3
Gigel si Andrei au N saci cu pietre pretioase numerotati de la 1 la N. Pentru ca nu vor sa imparta
pietrele intre ei, au hotarat sa joace un joc iar castigatorul sa ia toate pietrele. Jocul consta in
efectuarea alternativa a unor mutari. O mutare consta in selectarea unor pietre dintr-un
sac i (1 ≤ i < N) si mutarea acestora in sacul i+1. Jucatorul care nu mai poate efectua nici o
mutare (atunci cand toate pietrele sunt asezate in ultimul sac) pierde jocul.
Cerinta
Stiind atat numarul de saci si configuratia acestora, cat si faptul ca Gigel va efectua prima mutare
determinati, daca exista, o strategie de castig pentru el.
Date de intrare
Pe prima linie a fisierului joc3.in se va afla N, numarul de saci. Urmeaza N linii, pe linia i aflandu-
se numarul de pietre pretioase din sacul i.
Date de iesire
In cazul in care Gigel nu are strategie de castig fisierul de iesire joc3.out va contine pe prima linie
valoarea -1. Altfel, afisati pe prima linie numerele p si q ce codifica mutarea initiala pe care ar
trebui sa o efectueze Gigel pentru a castiga jocul (p este numarul sacului, iar q numarul de pietre).
In cazul in care exista mai multe mutari initiale ce ar putea duce la castigarea sigura a jocului,
afisati mutarea care are p minim. Daca totusi mai sunt cazuri de egalitate, afisati mutarea
cu q minim.
Restrictii si precizari
1 ≤ N ≤ 100.000
Lectia 5. Strategii de joc. Jocul NIM
Exemple
joc3.in joc3.out
5 2 2
2
5
4
3
1
Explicatie
Efectuand aceasta mutare Gigel va castiga daca va continua sa joace perfect. Nu exista alta
mutare cu p sau q mai mic.
joc3.in joc3.out
5 -1
0
0
0
0
10000
Explicatie
Cum toate pietrele sunt asezate in ultimul sac, Gigel nu poate efectua vreo mutare.
Solutie:
#include <fstream>
using namespace std;
ifstream fin("joc3.in");
ofstream fout("joc3.out");
int n, a, xo;
int v[100001];
int main()
{
fin >> n;
for (int i = 1; i < n; ++i)
{
fin >> v[i];
if ((n - 1) % 2 == i % 2) xo ^= v[i];
}
if (!xo) {fout << -1; return 0;}
for (int i = 1; i < n; ++i)
{
if ((n - 1) % 2 == i % 2)
{
if ((v[i] ^ xo) < v[i])
{fout << i << " " << v[i] - (v[i] ^ xo);
return 0;
}
else
{
if (v[i] + v[i - 1] >= (xo ^ v[i]))
{
fout << i - 1 << " " << (xo ^ v[i]) - v[i];
return 0;
}
}
Lectia 5. Strategii de joc. Jocul NIM
}
return 0;
}
Aiacujoc
În drumul lor către oraşul Zalău, Bulănel şi Bulăniţa au hotărât să joace un joc pentru a alunga
plictiseala.Cei doi au la dispoziţie un grid cu N linii (numerotate de la 1 la N) şi M coloane
(numerotate de la 1 la M). Bulănel începe jocul prin plasarea unui pătrat pe o celulă a gridului.
Scopul jocului este acoperirea gridului înaintea celuilalt jucător. La fiecare pas, un jucător alege o
direcţie (nord, est, sud, vest) si extinde figura rectangulară în direcţia respectivă cu maxim K linii
(dacă direcţia aleasă a fost nord sau sud) sau maxim K coloane (dacă direcţia aleasă a fost est sau
vest). La nici un moment al jocului, nu se poate extinde figura în afara gridului. Jucătorul care nu
mai poate extinde figura în nicio direcţie este declarat pierzător. După ce Bulănel plasează pătratul
iniţial, Bulăniţa e cea care face prima extindere.
De exemplu, dacă ar juca pe un grid de N = 3 linii şi M = 5 coloane şi ar alege K = 3, jocul s-ar putea
desfăşura în felul următor:
Pasul 1: Bulănel plasează pătratul pe Pasul 2: Bulăniţa extinde figura Pasul 3: Bulănel extinde figura cu 1 linie în
Pasul 4: Bulăniţa extinde figura Pasul 5: Bulănel extinde figura Pasul 6: Bulăniţa extinde figura
Bulănel pierde jocul acesta din cauză că nu mai poate extinde figura în nici o direcţie. Totuşi,
Bulănel ar fi putut câştiga jocul dacă ar fi făcut o serie diferită de mişcări (de exemplu, dacă ar fi
extins figura cu 2 coloane în vest în loc de 1, în pasul 5) sau dacă ar fi plasat pătratul iniţial pe o
altă poziţie.
Cerinţă
Cei doi hotărăsc să joace mai multe jocuri. Ajutaţi-l pe Bulănel să afle, pentru fiecare joc, numărul
de poziţii în care poate plasa pătratul iniţial astfel încât să aibă strategie de câştig, adică să poată
câştiga indiferent de mutările Bulăniţei.
Date de intrare
Fişierul de intrare aiacujoc.in conţine pe prima linie numărul natural T, reprezentând numărul de
jocuri pe care Bulănel şi Bulăniţa o să-l joace. Pe fiecare din următoarele T linii, se află câte trei
numerele naturale care definesc un joc: numărul natural N, reprezentând numărul de linii ale
gridului, urmat de numărul natural M, reprezentând numărul de coloane ale gridului, urmat de
numărul natural K, reprezentând numărul maxim de linii sau de coloane cu care un jucător poate
extinde figura la un anumit pas.
Lectia 5. Strategii de joc. Jocul NIM
Date de ieşire
Fişierul de ieşire aiacujoc.out va conţine T linii. Pe fiecare linie i, se va afla câte un număr natural
care reprezintă numărul de poziţii de pe grid unde Bulănel poate plasa pătratul iniţial, care îi
asigură lui Bulănel strategie câştigătoare pentru al i-lea joc.
Restricţii
Pentru toate testele, 1 ≤ T ≤ 10.
Pentru teste în valoare de 5 puncte, N = 1, 1 ≤ M ≤ 20, K ≥ max(N, M).
Pentru alte teste în valoare de 5 puncte, 1 ≤ N, M ≤ 20, K ≥ max(N, M), N şi M au parităţi
diferite.
Pentru alte teste în valoare de 10 puncte, 1 ≤ N, M ≤ 20, K ≥ max(N, M).
Pentru alte teste în valoare de 30 de puncte, 1 ≤ N, M ≤ 1 000, K ≥ max(N, M).
Pentru alte teste în valoare de 10 de puncte, 1 ≤ N, M ≤ 1 000 000, K ≥ max(N, M).
Pentru alte teste în valoare de 20 de puncte, 1 ≤ N, M, K ≤ 1 000 000.
Pentru alte teste în valoare de 10 de puncte, 1 ≤ N, M ≤ 1 000 000 000, 1 ≤ K ≤ 1 000 000.
Ambii jucători joacă optim ceea ce înseamnă că nu fac greşeli, anticipează mişcările
adversarului, iar dacă există o strategie de mişcări care să-i conducă spre câştig
atunci o vor folosi.
Problema va fi evaluată pe teste în valoare de 90 de puncte.
Exemplele vor reprezenta teste în valoare de 10 ("puncte din oficiu") şi vor fi cu
feedback.
Exemplu
aiacujoc.in aiacujoc.out
2 5
3 5 5 320
88 200 200
2 7
3 5 3 680
88 200 56
Explicaţie
Primul exemplu
Pentru primul joc, gridul are dimensiunile N=3, M=5. Un jucător poate extinde figura cu
maxim K=5 linii sau coloane. Poziţiile care îi asigură lui Bulănel strategie de câştig sunt (1;2), (1;4),
(2;3), (3;2), (3;4).
Pentru al doilea joc, gridul are dimensiunile N=88, M=200. Un jucător poate extinde figura cu
maxim 200 de linii sau coloane. Sunt 320 de poziţii care conduc la câştig.
Al doilea exemplu
Pentru primul joc, gridul are dimensiunile N=3, M=5. Un jucător poate extinde figura cu
maxim K=3 linii sau coloane. Poziţiile care îi asigură lui Bulănel strategie de câştig sunt (1;2), (1;4),
(2;1), (2;3), (2;5), (3;2), (3;4).
Pentru al doilea joc, gridul are dimensiunile N=88, M=200. Un jucător poate extinde figura cu
maxim 56 de linii sau coloane. Sunt 680 de poziţii care conduc la câştig.
Soluție:
#include <bits/stdc++.h>
using namespace std;
ifstream fin("aiacujoc.in");
ofstream fout("aiacujoc.out");
int t,n,m,k,i,x;
long long a[1<<21],b[1<<21];
int main()
{
fin>>t;
while(t--)
{
fin>>n>>m>>k;
for(i=0;i<(1<<21);++i) a[i]=b[i]=0;
Lectia 5. Strategii de joc. Jocul NIM
for(i=0;i<=k;++i)
{
if(i<n)
{
x=i^((n-i-1)%(k+1));
a[x]+=1+(n-i-1)/(k+1);
}
if(i<m)
{
x=i^((m-i-1)%(k+1));
b[x]+=1+(m-i-1)/(k+1);
}
}
long long sol=0;
for(i=0;i<(1<<21);++i) sol+=1LL*a[i]*b[i];
fout<<sol<<'\n';
}
return 0;
}
Cartonase
Miruna are N cartonase pe care le-a asezat in linie dreapta pe masa. Fiecare cartonas are o fata
colorata cu rosu, iar cealalta cu albastru. Miruna si fratiorul ei se gandesc la urmatorul joc:
O mutare valida consta din alegerea unui cartonas cu fata rosie in sus si intoarcerea
lui. In plus, daca doreste, cel care e la mutare poate sa isi aleaga orice alt cartonas
(indiferent de culoarea fetei care este in sus) care se afla la stanga celui ales initial
si sa il intoarca.
Cei doi copii efectueaza alternativ mutari valide.
Cerinta
Stiind ca cei doi copii vor juca optim, se cere sa se stabileasca castigatorul pentru o configuratie
data a cartonaselor.
Date de intrare
Pe prima linie a fisierului de intrare cartonase.in se gaseste un numar intreg T, reprezentand numarul
de seturi de date de test ce vor urma. Pe fiecare dintre urmatoarele T linii se va afla un numar
intreg N, urmat de un spatiu, apoi de N caractere despartite prin cate un spatiu ce pot fi R sau A,
semnificand culorile fetelor cartonaselor care sunt in sus.
Date de iesire
Fisierul cartonase.out va contine T linii. Pe linia i (1 ≤ i ≤ T) va fi scris mesajul DA in cazul in care Miruna
este cea care castiga jocul descris pe linia i+1 in fisierul de intrare, respectiv mesajul NU in caz
contrar.
Restrictii
1 ≤ T ≤ 20
1 ≤ N ≤ 100
Exemplu
cartonase.in cartonase.out
Lectia 5. Strategii de joc. Jocul NIM
2 DA
3 A R R NU
3 R R R
Soluție:
#include <iostream>
#include <fstream>
using namespace std;
ifstream fin("cartonase.in");
ofstream fout("cartonase.out");
int main()
{
int q,i,sum,n;char a;
fin>>q;
while(q--)
{
fin>>n;
sum=0;
for(i=1;i<=n;++i)
{
fin>>a;
if(a=='R') sum=i^sum;
}
if(sum) fout<<"DA\n";
else fout<<"NU\n";
}
return 0;
}
Probleme campion.edu.ro
# Nume ↓ Concurs ↓ Autor ↓ Grupă ↓ Dificultate ↑