Nevíte-li si rady s jakýmkoliv matematickým problémem, toto místo je pro vás jako dělané.
Nástěnka
❗22. 8. 2021 (L) Přecházíme zpět na doménu forum.matweb.cz!
❗04.11.2016 (Jel.) Čtete, prosím, před vložení dotazu, děkuji!
❗23.10.2013 (Jel.) Zkuste před zadáním dotazu použít některý z online-nástrojů, konzultovat použití můžete v sekci CAS.
Nejste přihlášen(a). Přihlásit
Dobrý den, chtěl bych váš poprosit o nějaký nápad (kód), pro tuto zákeřnou úlohu:
Je dána čokoláda o rozměrech 3xN čtverečků. Čokoláda má na některých čtvereččích umístěnu hrozinku. V zadání je dáno číslo N ("délka čokolády") a souřadnice [x;y] políček s hrozinkami. Určete počet způsobů, kolika můžeme rozdělit čokoládu na obdélníky 1x2, aby žádný z obdélníků neobsahoval hrozinku a tak byla čokoláda rozdělena na bezhrozinkové obdélníky 2x1 a čtverečky 1x1 s hrozinkou.
K zadání byl přiložen tento obrázek:
A jeho řešení: Pro čokoládu vlevo je 0 možností a pro čokoládu vpravo 3 možnosti.
Zkoušel jsem vyřešit případy, kdyby čokoláda hrozinky neobsahovala, a to pro N=2 a N=3. Přišel jsem na to, že pro případ N=2 jsou 3 možnosti a pro N=3 je 0 možností (je lichý počet políček, tak jedno vždy zbyde). Můj závěr pro čokoládu bez hrozinek je 3^(N/2) možností pro sudé N a 0 možností pro každé liché N. Bohužel ale nevím jak vytvořit algoritmus, který by to zjistil i pro čokoládu s hrozinkami :(.
Offline
To je hodně hustá úloha, ten algoritmus nebude jen tak jednoduchý. Pokud to dobře chápu, tak hrozinky nás vůbec neovlivňují, neboť jsou na poli 1x1. Takže nás pouze zajímá ta obdelníková část 2x1 bez hrozinek. Pokud najdeme čtvereček bez hrozinky a neexistuje k němu další (soused bez hrozinky), potom úloha nemá řešení, jinak má právě N řešení v daném cyklu.
Offline
Rozdělme čokoládu na "komponenty souvislosti" - každá komponenta je maximální souvislá plocha bez rozinky. Pak stačí vyšetřovat každou komponentu zvlášť a počet možných dělení je součin možných dělení u jednotlivých komponent.
Offline
↑ check_drummer:
Nad tím jsem také přemýšlel - rozdělit to prostě na kusy ohraničené hrozinkou, ale to bohužel úlohu také neřeší - ten můj vzorec nefunguje, pokud to má nějaký divoký tvar, takže nevím, jak to udělat :((
Offline
↑ check_drummer:A na každou komponentu souvislosti se můžeme dívat jako na bipartitní graf. Partity jsou indukovány "šachovnicovým" obarvením. Potom jde o to najít POČET úplných (někdy se jim říká perfektní) párování v bipartitním grafu (ne nutně pravidelném).
Řekl bych, že naprogramování algoritmu, který hledaný počet určí, bude jednodušší, než pokusit se sestavit obecný vzorec.
Offline
↑ Luky10:JPřiblížím tu grafovou formulaci.
Každému čtverečku bez hrozinky odpovídá vrchol grafu. Každý vrchol je spojen hranou s jiným vrcholem, pokud spolu na čokoládě sousedí hranou. Pokud by čokoláda byla barevná jako šachovnice, tak vždy sousedí černé čtverečky s bílými a naopak, nikdy černé s černými nebo bílé s bílými. Proto je graf bipartitní (jedna partita černé, druhá bílé čtverečky).
Pokud má některá komponenta lichý počet vrcholů, úloha nemá řešení. Pokud mají všechny komponenty sudý počet vrcholů, je potřeba pro každou komponentu najít (nejlépe rekurzívním algoritmem, nebo prohledáváním) počet různých párování a celkový počet je (jak už bylo řečeno) pak součinem počtu řešení pro jenotlivé komponenty.
Offline
Ten graf bych měl uložit do pole nebo pomocí pointerů?
Načrtl jsem si situaci pro konkrétní čokoládu:
Nevím sice, jak by se to dalo procházet rekurzivně, ale napadl mě tento způsob:
for i:=1 to pocet_vrcholu for j:=1 to pocet_vrcholu if (Sousedi_ij)=TRUE then vrchol[i]:=zabrany; vrchol[j]:=zabrany; endif; endfor; endfor; if Vse_zabrane()=TRUE then Jedno_Reseni_mame else Nemame zadne;
tehnle kód bych spouštěl do té doby, dokud by to nenalezlo všechnya řešení, ale jak přijdu na to, že mám všechna ? :(
Offline
Algoritmus ↑ petrkovar: je zřejmě korektní, ale jeho implementace vyžaduje přemýšlení.
Proto navrhnu "brute force" alternativu: uvažme tabulku o sedmi sloupcích, sloupce odpovídají podmnožinám množiny {1,2,3} o 0,1 a 2 prvcích. Na i-tém řádku ve sloupci P je počet způsobů, jimiž lze rozdělit prvních i-1 řad čokolády tak, že v i-té řadě je přesahujícími obdélníky pokryta podmnožina P. Každý řádek spočteme rekurzivně z předchozího. Stačí nám pamatovat si dva poslední řádky.
Jistě jde o polynomiální algoritmus.
Offline
Zkusil jsem to naprogramovat rekurzivne po radcich, a povedlo se (sice ne v Pascalu, ale to je jedno). Slozitost je linearni (funguje v klidu pro stovky tisic radku). Jestli s tim stale potrebujes pohnout, tak dej vedet.
Offline
↑ Lumikodlak: S tou linearitou ... při běžném přístupu "do integeru se vleze vše", ano. Ale pokud by byly opravdu stovky tisíc řádků a na nich (téměř) žádné hrozinky, může mít výsledek stovky tisíc cifer, bylo by proto poctivější mluvit o kvadratické složitosti. Předpokládám, že ty jsi měl hrozinek hodně, pokud jsi nenarazil na omezení intu :)
Offline
↑ Kondr:
Mas pravdu, tohle jsem nebral v uvahu, int mi samozrejme pretekl :-) Kdyz by nebyl potreba presny vysledek, staci pouzit desetinna cisla a s tema by problem nebyl. Pravde jsi ale blize ty.
Offline
↑ Lumikodlak:
Mohl bys ten kód prosíém někam umístit? (třeba sem na forum)
Sice už termín odevzdání vypršel, ale stejně mě velice zajímá, jak to vyřešit.
Dík
Offline
Tady to je, ale nevim, jestli je na prvni pohled jasne, jak to funguje. Muzu dat jeste nejake vysvetleni:
#include <stdio.h> typedef struct{ int PocetMoznosti; int VysledneRozvrzeni [3][3]; } Rozmisteni; int Cokolada [1000][3] = { {0,0,0}, {1,0,0}, {0,0,1}, {0,0,0}, {1,0,0}, }; int CelkovyPocetMoznosti, IndexRozmisteni, IndexMoznosti, PocetRadek = 5; int DocasnyRadek [3]; int MinuleMoznosti [2][2][2] = {0,0,0,0,0,0,0,1}; int SoucasneMoznosti [2][2][2]; Rozmisteni Rozvrzeni [2][2][2] = {{{ {3, 1,1,1 , 1,0,0 , 0,0,1 }, // 0 0 0 {2, 1,1,0 , 0,0,0 }},{ // 0 0 1 {1, 1,0,1 }, // 0 1 0 {1, 1,0,0 }}},{{ // 0 1 1 {2, 0,1,1 , 0,0,0 }, // 1 0 0 {1, 0,1,0 }},{ // 1 0 1 {1, 0,0,1 }, // 1 1 0 {1, 0,0,0 } // 1 1 1 }}}; int main(){ int CisloRadku; for (CisloRadku = 0; CisloRadku < PocetRadek; CisloRadku ++){ memset (SoucasneMoznosti, 0, sizeof(SoucasneMoznosti)); for (IndexRozmisteni = 0; IndexRozmisteni < 8; IndexRozmisteni ++){ if (MinuleMoznosti [0][0][IndexRozmisteni]){ for (IndexMoznosti = 0; IndexMoznosti < Rozvrzeni [0][0][IndexRozmisteni].PocetMoznosti; IndexMoznosti ++){ memcpy (DocasnyRadek, Cokolada [CisloRadku], sizeof(DocasnyRadek)); DocasnyRadek [0] += Rozvrzeni [0][0][IndexRozmisteni] . VysledneRozvrzeni [IndexMoznosti][0]; DocasnyRadek [1] += Rozvrzeni [0][0][IndexRozmisteni] . VysledneRozvrzeni [IndexMoznosti][1]; DocasnyRadek [2] += Rozvrzeni [0][0][IndexRozmisteni] . VysledneRozvrzeni [IndexMoznosti][2]; if (DocasnyRadek [0] > 1 || DocasnyRadek [1] > 1 || DocasnyRadek [2] > 1) continue; SoucasneMoznosti [DocasnyRadek [0]] [DocasnyRadek [1]] [DocasnyRadek [2]] += MinuleMoznosti [0][0][IndexRozmisteni]; } } } memcpy (MinuleMoznosti, SoucasneMoznosti, sizeof(MinuleMoznosti)); } CelkovyPocetMoznosti = MinuleMoznosti[0][0][1] + MinuleMoznosti[1][0][0] + MinuleMoznosti[1][1][1]; printf ("\nPocet moznosti je %ld", CelkovyPocetMoznosti); }
Offline
Díky moc ;)
Měl jsem potíž s funkcemi memset() a memcopy(), našel jsem si jejich princip a myslím, že jsem ho pochopil - daly by se ty funkce (popořadě) nahradit tímto?:
for (i=0;i<=3;i++) { for (y=0;y<=3;y++) { for (x=0;x<=3;x++) { SoucasneMoznosti[i][y][x]=0;}}}
for (i=1;i<=3;i++) {DocasnyRadek[i]=Cokolada [CisloRadku][i];}
for (i=0;i<=3;i++) { for (y=0;y<=3;y++) { for (x=0;x<=3;x++) {MinuleMoznosti[i][y][x]=SoucasneMoznosti[i][y][x];}}}
Dále bych se chtěl zeptat, jestli by jsi mi nemohl vysvětlit jak generuješ obsah polí Rozvrzeni a MinuleMoznosti na základě pole Cokolada.
Děkuji předem za další odpovědi
Offline
Nahradit by to slo, akorat ty cykly trochu upravit, takze takhle:
for (i=0;i<2;i++) { for (y=0;y<2;y++) { for (x=0;x<2;x++) { SoucasneMoznosti[i][y][x]=0;}}}
for (i=1;i<3;i++) {DocasnyRadek[i]=Cokolada [CisloRadku][i];}
for (i=0;i<2;i++) { for (y=0;y<2;y++) { for (x=0;x<2;x++) {MinuleMoznosti[i][y][x]=SoucasneMoznosti[i][y][x];}}}
Rovrzeni a MinuleMoznosti negeneruju na zaklade Cokolady, ty jsou tam napevno, cili porad stejne pro jakoukoliv Cokoladu.
To pole Rozvrzeni udava, jak muzes rozmistit ty 2x1 obdelniky tak, abys zaplnil cely radek. Takze napriklad mas na radku prvni dva ctverecky volne, a treti obsazeny, takze [0][0][1] (okomentovano // 0 0 1). Kdyz chces tenhle radek zaplnit pomoci 2x1 obdelniku, tak bud tam das jeden horizontalne (takze na dalsim radku budes mit 0 0 0) anebo vlevo dva vertikalne (takze na dalsim budes mit 1 1 0). To {2, 1,1,0 , 0,0,0 } znamena jak muze vypadat ten dalsi radek (2 jako dve moznosti).
MinuleMoznosti [2][2][2] = {0,0,0,0,0,0,0,1} to jsou jakoby pocatecni podminky - pro nulty radek.
Neni to moc jednoduche vysvetlit, tak jsem to asi moc dobre nevysvetlil :-)
Offline
Někde asi bude chyba, tento kód:
#include <stdio.h> typedef struct{ int PocetMoznosti; int VysledneRozvrzeni [3][3]; } Rozmisteni; int Cokolada [1000][3]; int CelkovyPocetMoznosti, IndexRozmisteni, IndexMoznosti, PocetRadek; int DocasnyRadek [3]; int MinuleMoznosti [2][2][2] = {0,0,0,0,0,0,0,1}; int SoucasneMoznosti [2][2][2]; Rozmisteni Rozvrzeni [2][2][2] = {{{ {3, 1,1,1 , 1,0,0 , 0,0,1 }, // 0 0 0 {2, 1,1,0 , 0,0,0 }},{ // 0 0 1 {1, 1,0,1 }, // 0 1 0 {1, 1,0,0 }}},{{ // 0 1 1 {2, 0,1,1 , 0,0,0 }, // 1 0 0 {1, 0,1,0 }},{ // 1 0 1 {1, 0,0,1 }, // 1 1 0 {1, 0,0,0 } // 1 1 1 }}}; int main(){ int i,y,x; PocetRadek=5; for (i=0;i<PocetRadek;i++){for (y=0;y<=2;y++){ Cokolada[i][y]=0;}} Cokolada[2][0]=1; Cokolada[2][1]=1; Cokolada[2][2]=1; int CisloRadku; for (CisloRadku = 0; CisloRadku < PocetRadek; CisloRadku ++){ for (i=0;i<2;i++) { for (y=0;y<2;y++) { for (x=0;x<2;x++) { SoucasneMoznosti[i][y][x]=0;}}} for (IndexRozmisteni = 0; IndexRozmisteni < 8; IndexRozmisteni ++){ if (MinuleMoznosti [0][0][IndexRozmisteni]){ for (IndexMoznosti = 0; IndexMoznosti < Rozvrzeni [0][0][IndexRozmisteni].PocetMoznosti; IndexMoznosti ++){ for (i=1;i<3;i++) {DocasnyRadek[i]=Cokolada [CisloRadku][i];} DocasnyRadek [0] += Rozvrzeni [0][0][IndexRozmisteni] . VysledneRozvrzeni [IndexMoznosti][0]; DocasnyRadek [1] += Rozvrzeni [0][0][IndexRozmisteni] . VysledneRozvrzeni [IndexMoznosti][1]; DocasnyRadek [2] += Rozvrzeni [0][0][IndexRozmisteni] . VysledneRozvrzeni [IndexMoznosti][2]; if (DocasnyRadek [0] > 1 || DocasnyRadek [1] > 1 || DocasnyRadek [2] > 1) continue; SoucasneMoznosti [DocasnyRadek [0]] [DocasnyRadek [1]] [DocasnyRadek [2]] += MinuleMoznosti [0][0][IndexRozmisteni]; } } } for (i=0;i<2;i++) { for (y=0;y<2;y++) { for (x=0;x<2;x++) {MinuleMoznosti[i][y][x]=SoucasneMoznosti[i][y][x];}}} } CelkovyPocetMoznosti = MinuleMoznosti[0][0][1] + MinuleMoznosti[1][0][0] + MinuleMoznosti[1][1][1]; printf("Moznosti: %i",CelkovyPocetMoznosti); }
Mi vypíše "Moznosti: 0" :(
Offline
Toho jsem si drive nevsimnul - nema byt
for (i=1;i<3;i++) {DocasnyRadek[i]=Cokolada [CisloRadku][i];}
ale
for (i=0;i<3;i++) {DocasnyRadek[i]=Cokolada [CisloRadku][i];}
snad takhle je to ok
Offline