SlideShare uma empresa Scribd logo
1 de 134
Baixar para ler offline
Programozói versenyfeladatok,
alapvető matematikai algoritmusok




        Kiss Csaba Zsolt
  Programtervező matematikus
             KLTE
            Debrecen




             1994.
Tartalomjegyzék


   Bevezetés                                                  2
   A versenyekről.                                            3
   1. Kombinatorikai algoritmusok                             6
       1.1. Ismétlés nélküli permutációk.                     6
       1.2. Ismétléses permutációk                           10
       1.3. Kombinációk                                      13
       1.4. Egy halmaz összes részhalmazai, particionálása   16
       1.5. Gray kódok                                       23
   2. Geometriai algoritmusok                                26
       Bevezetés                                             26
       2.1. Szakaszok metszete                               28
       2.2. Poligon és pont                                  31
       2.3. Ponthalmaz konvex burka                          34
   3. Gráfalgoritmusok                                       39
       Bevezetés                                             39
       3.1. Mélységi keresés                                 40
       3.2. Optimális keresés                                44
   Függelék                                                  52
       Ábrák, programok listája                              52
       Problémák listája                                     52




                                                                  1
Bevezetés

A dolgozat címében hivatkozott versenyfeladatok az ACM szervezésében rendezett egyetemi
programozói csapatversenyek feladatai. A versenysorozatot évente rendezik és három fordulóból áll.
Az első forduló általában valamilyen helyi (egyetemi, városi) verseny. Ezeket a versenyeket a helyi
(lelkes) szervezők szervezik, általában önkéntes alapon. Szervezésen itt a          verseny tényleges
lebonyolítását kell érteni és a feladatok kiválasztását. A helyi versenyekről tovább jutó csapatok
vesznek részt a második fordulóban (egyetemenként legfeljebb egy csapat), amelyet már az ACM
adott szervezete szervez. A második fordulóban (Regional Final - területi döntő) általában valamilyen
nagyobb területi egység (Pl. Kalifornia, Nyugat-Európa) egyetemeinek csapatai versenyeznek. A
területi döntőkből egy-három csapat juthat tovább a döntőbe, amely mindig valamelyik USA-beli
nagyvárosban zajlik.
A dolgozatom célja a verseny és a verseny feladatainak általános bemutatása. Egyetemi éveim alatt
összesen 7 -szer vettem részt 1. és 2. fordulós versenyeken. Az itt összegyüjtött tapasztalatokat és
szép megoldásokat, a megoldásokhoz felhasmált alapvető matematikai algoritmusokat úgy éreztem
mindenképpen szükséges rendszerezve megörökíteni, ehhez kínált kiváló fórumot a diploma
dolgozatom.
A dolgozat melléklete kb. 90 eredeti versenyfeladatot tartalmaz, amely biztosan nagyon hasznos lesz
az egyetemünkön folyó első és másod éves programozó-matematikus, matematikus, informatika és
matematika tanár szakos hallgatók különböző tantárgyainak gyakorlati oktatásához. . A feladatok
angol nyelvűek, mert a versenyek hivatalos nyelve az angol.
A dolgozatban található alapvető matematikai algoritmusok segítségével mintegy 30 (általában a
 nehezebbek közé sorolt probléma) oldható meg. Az algoritmusok kisebb módosításaival további
 feladatok megoldhatók. Az egyes algoritmusoknál hivatkozás található a feladatokra, amelyek
 megoldásához az adott algoritmus jó hatásfokkal felhasználható. Az egyes algoritmusok újabb
 problémákat vetnek fel (P-jelű problémák) ezek megoldása szintén jó gyakorlat lehet.
 Ezúton szeretnék a csapatom nevében köszönetet mondani szakmai támogatóínknak és tanárainknak
 akik nélkül még az így elért szerény eredményeinket sem értük volna el, név szerint: Kuki Attilának a
 helyi versenyek szervezéséért és az európai döntőre való kiutazás szervezéséért, valamint hasznos
 tanácsaiért, Dr Arató Mátyásnak, Dr Lajkó Károlynak, Dr. Juhász Istvánnak és Dr Végső Jánosnak
 általános támogatásaikért, Herendi Tamásnak igen hasznos szakmai tanácsaiért és az IFSz Kft.
 valamint az IQSoft Rt. munkatársainak szakmai és egyéb támogatásukért. Végül szeretnénk
 megköszönni anyagi támogatásukat azon cégeknek és szervezeteknek akik nélkül semmiképpen sem
 képviselhettük volna egyetemünket az európai döntőkön:

A Külkereskedelmi és Hitel Bank Rt.,
A Biogal Rt.,
Az IQSoft Rt.,
A Dataware Kft.,
A KL TE Diákönkormányzata.

Valamint     szeretném     megköszönni      csapattársaimnak Fekete Zoltánnak, Jakó Jánosnak,
Molnár Tamásnak és tanáraimnak Kuki Attilának és Herendi Tamásnak a dolgozat elkészüléséhez
adott hasznos tanácsaikat és bölcs észrevételeiket.




                                                                                                     2
A versenyekről

A verseny szabályai:

A versenyeken kezdetben 4, majd (1991 után) 3 fós csapatok indulhattak. A csapat tagjai között
kezdetben lehetett egy diplomás is, később diplomások részvétele nem volt megengedett. A verseny
ideje általában 5 óra. A csapatok a verseny ideje alatt egy darab IBM PC típusú számítógépet
használhatnak a szükséges fordítóprogrammal felszerelve, ez a TURBO PASCAL 5.0 -ás verziója
volt. A csapatok a verseny alatt bármilyen írott forrást használhatnak, viszont semmilyen más (pl.
mágneses) forrás használata nem megengedett csakúgy, mint a programozható zsebszámológépek
használata sem. A csapatoknak a verseny ideje alatt 5-8 problémát kell megoldaniuk. A problémák
között semmilyen sorrendiség sincs. A verseny nyelve angol, ezért a feladatok szövege, a zsűrinek
feltett kérdések, a zsűri válaszai is angol nyelvűek.

A problémák megoldása

A csapatoknak az elkészült megoldást -amely mindig egy pascal program forráskódja - az e célra
fenntartott mágneslemezen kell a zsűrihez eljuttatniuk. A zsűri a kódot lefordít ja és saját input
adataival teszteli. A programoknak 1 perc futásidő áll rendelkezésére, ez alatt kell outputot
produkálniuk. Az output alapján a zsűri a következő válaszokat adhatja a csapatoknak:

1. Syntax Error - fordítási hiba
2. Run Time Error - Futás közbeni programhiba, pl O-val való osztás
3. Time Limit Exceded - Időtúllépés
4. Wrong Answer - hibás válasz.
5. Accepted - Elfogadva

Egy probléma megoldásával többször is lehet próbálkozni, de zsűri csapatonként és problémánként
méri a verseny kezdetétől a megoldáshoz felhasznált időt. Az 1.-4. esetben (és minden további
sikertelen kísérlet után) a zsűri az adott csapatnak adott probléma megoldásához felhasznált idejét 20
perccel növeli.
A verseny ideje alatt az egyes feladatokkal kapcsolatban felmerült értelmezési stb. problémákkal
kapcsolatban a csapatok írásban kérdéseket tehetnek fel a zsűrinek, aki szintén írásban köteles
válaszolni ezekre. A zsűri a csapatokat szabályszegés esetén kizárással sújthatja.

A Kiértékelés szabályai:

A verseny végeztével a zsűri összeszámolja az egyes csapatok által megoldott problémák számát, és
összeadja a helyesen megoldott problémákhoz felhasznált időket. Így minden csapat eredménye két
mennyiségből áll:
         - A megoldott problémák száma
         - Az ehhez felhasznált idő

 Az a csapat a verseny győztese, amely a legtöbb feladatot oldotta meg, ha ilyen több van akkor a
 verseny győztese az a csapat, amely a legkevesebb időt használta fel.

 Hazai Versenyek

 Az egyetemi programozói versenyek története Magyarországon 1990-ben kezdődött egy a Budapesti
 Műszaki Egyetemen (BME) rendezett versennyel, ahol nemcsak a BME csapatai, hanem a fóvárosi
 egyetemeken kívül vidéki csapatok is indultak. Ezen a versenyen a KL TE 3 csapatot indított, melyek
 a középmezőnyben végeztek. 1991 után minden évben a fóvárosban és Debrecenben is rendeztek
 versenyeket. Ezeken a versenyeken az induló csapatok száma nagyjából állandó volt: Budapesten kb
 25, míg Debrecenben kb 10.



                                                                                                     3
A versenyek tapasztalatai

A következő néhány mondatban a versenyeken, a problémák megoldásával kapcsolatban szerzett
tapasztalatokról szeretnék írni. A verseny kezdetén érdemes minden feladatot átolvasni és értelmezni.
Az egyes csapatokon belül többféle megoldási módszer is kialakulhatott, az egyik lehetséges, hogy az
értelmezés után a csapattagok egymás között szétosztják a feladatokat és eztán egyenként, vagy
problémás feladat esetén együtt keresik a megoldást és valósítják meg a kivitelezést. A másik
módszer, hogy minden feladat elvi megoldását a csapattagok együtt keresik, csak a konkrét
megvalósítás ideje alatt dolgoznak különböző feladatokon a csapattagok. De ezektől különböző más
módszerek is kialakulhattak, valószínűleg erre nincs általános alkalmazható stratégia. A feladatok
megoldásakor szerenesés esetben, amikor egyszerre több feladat elvi megoldása is elkészült, a szűk
keresztmetszetet a rendelkezésre álló egyetlen számítógép gépideje (5 óra) jelenti. Érdemes a
 legkönnyebb, legegyszerűbb probléma megoldásának megvalósításával kezdeni. A megoldásokban
nem kell szépségre és az eleganci ára törekedni, mert a zsűri ezt nem értékeli. Sokkal inkább a kód
 egyszerűségére és átláthatósága kell, hogy a cél legyen. A feladatok elég nagy része többféleképpen is
 megoldható. Azoknál a feladatoknál, ahol a megoldást el lehet érni a feladatbeli objektumok összes
 esetének (pl. összes permutáció) vizsgálatával, ott a program rendelkezésére álló 1 perces futási időre
 kell figyelnünk, azaz tisztában kell lennünk az átlagos PC-k (különösen a zsűri által használt PC)        .:»
 gyorsaságával. Viszont ha az összes eset vizsgálata belefér az egy perces futási időbe, nem érdemes a
 szép és "eszes" megoldás megkeresésévei foglalkozni. A későbbiekben az adott helyen az egyes
 algoritmusok futási idejére utalni fogunk.

A mellékletben található feladatok

A mellékletben található feladatokra a dolgozatban a feladatok azonosítójával hivatkozunk, amely
XXXXéé-n alakú, ahol XXXX a verseny helyszínének rövidítése, éé a verseny megrendezésének
évszáma évszázad nélkül, n pedig a feladat sorszáma. A mellékletben a feladatok évszám szerint
növekvő sorrendben találhatók. A formátumuk eltér az eredetitől, hogy egységesen kezelhessük őket.
A legnagyobb része a feladatoknak négy jól körbehatárolható csoportból kerül ki. Ezek a
következőek:

1. Szimulációs feladatok
Ezekben a feladatokban jól definiált objektumokkal találkozhatunk, amelyekhez szabályok tartoznak.
Az objektumok ezen szabályok szerint viselkednek. A megoldáshoz nem kell egyéb, mint a feladat
pontos megértése, az objektumok megfelelő gépi reprezentál ása és a szabályok pontos programozása.

       Könnyebb szimulációs problémák: USSC85-3, KLTE91-1, RUGB91-1, RUGB91-2,
        ACMF91-5, IOAG91-1, ODUN92-1, RUGB92-2, RUGB92-5, KLTE92-2, USEC92-
        2, ACMF92-1, KLTE93-4

       Nehezebb szimulációs problémák:KLTE91-2,            RUGB91-5,       RUGB91-7,       ACMF91-3,
        ACMF91- 4, RUGB92 -6, TUBP92-1,

A szimulációs feladatok megoldásával a dolgozatban nem foglalkozunk. Megoldásukat a Pascal
nyelvvel ismerkedőknek ajánlhatjuk. Megoldásukhoz csak alapvető matematikai ismeretekre van
szükség.

 2. Kombinatorikai feladatok
 lásd a dolgozat első fejezetét

 3. Geometriai feladatok
 lásd a dolgozat második fejezetét

 4. Gráfelméleti problémák
 lásd a dolgozat harmadik fejezetét.

                                                                                                       4
Az egyes versenyek helyszínei és időpont ja:


Rövidítés    Színt           Helyszín                   Időpont
             (forduló)
USSC85       2               ?, California , USA        1985.
KLTE91       1               KL TE, Debrecen            1991. Június
IOAG91       *               Athen, 3rd International   1991. Május
                             Olympiad in Informatics
TUBP91       1               BME, Budapest              1991. Október
RUGB91       2               Gent, Belgium              1991. November
ACMF91       3               USA                        1991.
USSC92       2               ?, USA                     1992.
ODUN92       1               Norfolk, Va USA            1992. Szeptember
TUBP92       1               BME, Budapest              1992. Október
KLTE92       1               KL TE Debrecen             1992. Október
RUGB92       2               Gent, Belgium              1992. November
ACMF92       3               Indíanapolis, USA          1992.
KLTE93       1               KL TE, Debrecen            1993. Szeptember




                                                                           5
1. Kombinatorikai algoritmusok

1.1. Ismétlés nélküli permutációk

Vizsgáljuk először az ismétlés nélküli permutációk generálásának problémakörét. A probléma pontos
defmíciója a következő:

Adott a P={ 1,2 ..n} halmaz, előállítandó

a) az összes permutációja tetszőleges sorrendben.
b) az összes permutáció ja lexikografikus sorrendben.
c) a lexikografikus rendezés szerinti i-edik permutációja

A P összes permutációinak halmazát jelöljük P! -al : P! = {~, pz 'o 00' Pn! }
Először az egyik legegyszerubb módszer bemutatásával kezdjük. A módszert Fike publikálta 1975-
ben [Fike1975] , majd 1976-ban Rohl módosította [Roh1l976] .
Legyen S={(d2,d3,      ••• ,dn) II~dk s k: k=2,3, .. n} ekkor S összesen 2*3* ..*n=n! vektort
tartalmaz. Vegyük észre, hogy S elemeit programmal könnyen lehet generálni: kis n esetén n-l darab
egymásba ágyazott ciklussal, ahol a ciklusváltozók értékei rendre az [1..2], [1..3], ... ,[1..n]
intervallumokat futják be, nagy n esetén rekurzívan. Ha egyszeruen programozható egy-egy értelmű
megfeleltetést adnánk S és P! elemei között akkor, mivel S elemeit könnyen generálhatjuk egyszeru
módszert kapnánk P! generálására. A Fike módszere a következő egy-egy értelmű megfeleltetést adja
S és P! elemei között: Legyen (d2, d3, ••• , dn) egyelem S-ből, ekkor a hozzátartozó P permutációt
úgy kapjuk, hogy kiindulva ~ = (1,2, ... , n) - ből, mint kezdeti permutációból cseréljük fel P; -ben a
k-adik elemet a dk -adikkal.
Mindezek alapján az algoritmus először az S-beli elemeket generálja, majd ebből állítja elő a fent
leírtaknak megfelelően a kapcsolódó permutációt.
Észrevehető, hogy a fenti algoritmus redundáns elemeket tartalmaz (pl ha dk = k, akkor felesleges
csere), ezen elemek kiküszöbölésére tett módosítást 1976-ban J. S. Rohl. A Rohl által módosított
algoritmus pascal programja a következő:

proeedure    Fike_Rohl_perm(n:integer);
var p:array[l ..max) of integer; { n <= max}
     i:integer;
proeedure permute(k:integer);
var temp, dk, dn:integer;
begin
 if k=n then begin
  proe (p) ;
   temp: =p [nl ;
   for dn:=n-l downto 1 do
    begin
     p[n) :=p[dn) r p l dn ) :=temp;
     p.r t p j r
        oc
       p j dn l r=p I n l r
     end;
     p[n) :=temp;
    end
     else begin
        permute (k+l) ;
        temp :=p [k) ;
        for dk:=k-l downto              1 do
         begin
          p [k) :=p [dk) ;


                                                                                                      6
p[dk] :=tempi
            permute(k+l)i
            p [dk] :=p [k] i
           e rid r
         p[k] :=tempi
   endi
 endi
 be gin {Fike_Rohl perm}
  for i:=l to n do p[i] :=ii
  permute(2)i
 endi

        Fig. 1.1.1..' Fike algoritmusa Rohl módosításaival

A versenyfeladatok megoldásánál a közölt eljárás általában jól használható, de azokban a
problémákban ahol a feladat szempontjából n állandó és n nem túl nagy (n<6), elképzelhető olyan
eljárás is amely n darab egymásba ágyazott ciklust tartalmaz az S elemeinek generálásához. Ennek
az algoritmusnak a gyorsaság mellett a kód egyszerűsége is az előnye.
A fenti módszer csupán az (a) problémára ad választ. Ha pl. valamely feladat a lexikografikus
sorrendben követeli meg tőlünk a permutációk felsorolását, akkor a módszerek egy újabb családjával
kell megismerkednünk [We1ll971] . Most egy olyan módszert mutatunk be, amely 1812-ből
származik, első említése [FiscI812] majd [ShenI962] . A módszer lényege négy lépés alkalmazása
                 =
egy adott P (Pl' P2'''' Pn) permutációra, amely eredményeként a lexikografikusan következö
permutációt kapjuk

A négy lépés:

(1)   Legyen i a legnagyobb index, amelyre p i-l< Pi
(2)   Legyen} az a legnagyobb index, amelyre Pi-I<Pj
(3)   Cseréljük fel Pi-l -et p.-vel.
(4)   Fordítsuk meg Pl' Pi+I,··.,Pnsorrendjét.

        Fig. 1.1.2 ..'A lexikografikus felsorolás   négy lépése

Pl. 1.1. Írjunk olyan pascal programot, amely az 1.1.2 ábra alapján lexikografikus sorrendben
generálja egy halmaz partícióit

Most egy másik, a permutációkat lexikografikusan felsoroló módszer mutatunk be. P! egy általános
elemének generálásakor az összes N={l,2 ..n} számnak hozzá kell rendelődnie a Pl -hez, majd az
N  {Pl} -beli összes elemnek hozzá kell rendelődnie a P2-höz, és így tovább. Ezek alapján
algoritmusunk szerkezete a következő: Ahhoz, hogy a permutációkat lexikografikus sorrendben
kapjuk korlátoznunk kell az egyes Pj -k kiválasztásának sorrendjét. Ha a választható elemek közül
elsőként mindig a kisebbiket választ juk, akkor a permutációkat lexikografikusan növekvő sorrendben
kapjuk.
Kézenfekvő, hogy algoritmusunkban a választható elemeket egy listában tároljuk, a listák kezelése
(elem-törlés, -beszúrás, stb.) a pascal nyelvben a jól ismert mutatós módszerrel talán túl sok
adminisztrációs lépéssei járna, ezért kihasználva a jelen probléma specifikurnát a megfelelő listát egy
tömbbel szimuláljuk, legyen ez a:array[O ..n} o/integer. A tömb egy elemének indexe reprezentálja
i-edik listaelem által tárolt értéket, míg maga az elem a lista következő elemére mutat. Vagyis pl. az
[1,3,3,4,6,6,0] tömb az 1 ~ 3 ~ 4 ~ 6 listát reprezentálja.




                                                                                                      7
Ekkor az a[O..n] inicializálását, feltöltését a következő eljárás végzi:

procedure Init;
var i: integer;
begin
 for i:=O to n-l do a[i):=i+l;
 a[n):=O;
end;

Tegyük fel, hogy p[1..n] egy globális tömb var p:array[1 ..n} of integer defmícióval, a permutációk
tárolásához, valamint már létezik a PrintPerm eljárás a kész permutációk megjelenítéséhez. Ekkor a
permutációkat lexikografikusan felsoroló algoritmus pascal kódja a következő:

procedure      enum(i:integer);
var t:integer;
begin
 t:=Oi
 while a[t)<> O do
 begin
  p[i]:=a[t];
   if i<> n
  then
     begin
      a [t] :=a [a [t]];
      enum(i+l) ;
      a [t] : =p [i] ;
    end
   else
    Printperm;
   t:=a[t];
 end;

       Fig. J. J. 3.: Permutációk   lexikografikusan

Az algoritmusunknál alkalmazott gondolatmenet, mint majd látni fogjuk ismétléses permutációkra is
általánosítható lesz.
A fenti algoritmusok teljesítménye között a versenyfeladatok megoldásának szempontjából lényeges
különbség nincs. Ezen azt kell érteni, hogy a megoldások a futásra felhasználható idő (1 perc) alatt
nagyjából n=ll-ig képesek az összes permutációt előállítani. Természetesen a fenti módszereken
kívül számos más módszer is ismeretes, melyek más-más célra használhatók a legalkalmasabban. A
különböző algoritmusok több szempontú összehasonlításával foglalkozik Roy [RoyI978] és Ives
[Ivesl976].
A probléma (c) részében megfogalmazottakra mind Fike [FikeI975], mind Wells [WeIIsi971] kínál
megoldást. A (c)-ben megfogalmazott probléma speciális esete (n=k) annak a problémának amikor
egy n elemű halmaz k-ad osztályú kombinációinak összes permutációit rendezzük lexikografikusan és
ezek között keressük a i-ediket. Ennek az általánosabb problémának a megoldása a "Kombinációk"
című fejezetben található.
A (c) problémára ezen kívül hasznos eligazítást találhatunk [BrowI970]    -ban is.


Irodalomjegyzék         az 1.1. fejezethez

 [FikeI975]:       C. T. Fike (1975). A permutation generation method. The Computer Journal, Vol.
                   18,p21.
 [Rohll976]:       1. S. Rohl (1976). Programming improvements to Fike's algorithm for generating
                   permutations. The Computer Journal, Vol. 19, p 156.

                                                                                                    8
[WeIll 1971]:   M. B. Wells (1971). Elements ofCombinatorial Computing. Pergamon Press,
                NewYork
[Fisc1912]:     L. L. Fischer and K. Chr Krause (1812). Lehrbuch der Combinationslehre und der
                Arithmetik. Dresden.
[Shen1962]:     Shen, Mok-Kong (1962). BIT Vol. 2. p. 228.
[RoyI978]:      M. K. Roy. (1978). The Computer Journal, VoI2l., p. 296.
[Ivesl976]:     F. M. Ives. (1976).Permutation Enumeration: Four New Permutation Algorithms
                CACM, Vol. 19., Nr. 2., p. 68.
[Brow 1970]:    R. M. Brown: Decoding Combinations of the First n Integers Taken k at a Time.
                CACM Vol. 3-4 p 235.




                                                                                                 9
1.2. Ismétléses permutációk

A probléma pontos definíciója a következő:
Adott 1 <= r <= n pozitív egész (n darab, r különböző                   elem permutációit keressük),
                                                                  r

valamint azF   = (ft .t; ···,fr')       vektor, ahol n       = LJ;         és 1::;; J; (i   = 1,2, ... r)
                                                                  ;=1

generálandó az M    = {u,..,1,2,2, .. ,2, .... ,r,r, .. ,r}
                        ~           ~                 '---v---'
                            ft          h               /,
halmaz összes (ismétléses) permutációja.
Egy ilyen permutációt jelöljünk csakúgy, mint az előzőekben P = (Pl' P2"" Pn)-vel.
A P-t generáló algoritmusunk egybeesik azzal a módszerrel ahogyan "kézzel" felírnánk a fenti
permutációkat: Válasszunk M-ből egy elemet az összes lehetséges módon ez lesz Pl' minden egyes
ilyen választás után válasszunk egy elemet M{Pl}-ből                      P2-helyére, ..., és végül minden egyes Pn-l
kiválasztása után Pn helyére válasszunk M  {Pl' P2"'" Pn-l} -ből, mint az előző fejezetben. Ha r=n
akkor az ismétlés nélküli permutációkat kapjuk. Ha az 'összes lehetséges módon' történő választást
az elemek növekvő sorrendjében végezzük el, akkor a permutációkat is lexikografikusan ebben a
sorrendben kapjuk. Rohl 1978-ban publikálta [Roh1l978] a fenti módszert némi általánosítással: Ha
az algoritmus során a kiválasztásokat nem végezzük el csak Ps -ig (S < n )-ig akkor n-elem s-ed
osztályú kombinációinak (ismétléses) permutációit (s-permutációit) kapjuk csakúgy, mint az előző
fejezetben.
Végül algoritmusunk programja a következő:

procedure genperm(m,f:vect;r,rO:integer);
const max=20;
type vect=array[l ..max] of integer;

var
 p:intvect;
 k:integer;

procedure choose(k:integer);
var
 i:integer;
begin
 for i:=l to r do
  if f[i] <> O then
  begin
    p [k] : =m [i] ;
    dec(f[i]);
    if k<>rO then choose(k+l) else proc(p);
    inc(f[i]);
  end
end;

 begin {genperm}
 choose(l);
 end;

       Fig. 1.2.1.: Rohl algoritmusa        (1978).




                                                                                                                     10
Ha valamely n elem ismétléses permutációi közül a lexikografikus sorrendben pontosan az i. -re van
sziikségünk akkor az ezt előállító algoritmust Wells [Welli971] munkájában találjuk. Készítsük most
el az i-edik lexikografikus ismétléses permutációt generáló algoritmus saját verzióját. Kiindulásként
alkalmazzuk Wells az "inverzfeladat"-ot megoldó algoritmusát [We1ll971] . Ez az algoritmus az
inputjaként egy permutációból előállítja az adott permutáció lexikografikus sorrendbeli sorszámát:


const max=100i
type intvect=array[O            ..max]      of longinti

function nalatt     k (n:longintik:longint)                      :longinti
 Var
  i      :integer i
  result:longinti
 Begin
  result:=li
  if k<>O then
    for i:=O to (k-1) do result:=(result                         div   (i+1)   )*(n-i)i
  nalatt   k:=result
  endi

  function iperm2num(n,r:integerif,p:intvect)                          :longinti
  (* osszesen k-1 fele objektumunk van,
     osszesen n darab objektumunk van,
     n=f [O]+f [1]+ ...+f [r-1]
      f[j]: a j. objektumból    f[j] darab van                         O <= j <= r-1
  *)

  var
    H,MM,J:intvecti
    q,i,jj:integeri
    nn,v:longinti
  begin
   for i:=O to r-1 do begin h[i] :=OiMM[i] :=Oij[i] :=1 endi
   for i:=O to n-1 do
   begin
    MM[p[i]] :=MM[p[i] ]+n_alatt k(h[p[i]],j [p[i]]) i
     inc(h[p[i]]) iinc(j [p[i]]) i
     for q:=O to p[i]-l do inc(h[q])i
    e nd r
    v:=liNN:=Oiq:=f[r-1]i
    for jj:=r-2 downto O do
     begin
      NN:=NN+MM[jj]*Vi
      q:=q+f[jj];
      v:=v * n_alatt k(q,f[jj]);
     end;
    iperm2num:=NN
  endi

         Fig. 1.2.2.: Wellsféle JPERM2NUM   foggvény   (1971).




                                                                                                    II
A mi feladatunk azonban olyan algoritmus írása, amely a sorszám alapján "legyártja" a hozzá
tartozó (ismétléses) permutációt.
Vegyük észre, hogy adott M és resetén M bármely MO részhalmazának a lexikografikusan első
(jelöljük [MO]F-al) ill. lexikografikusan utolsó permutációja (jelöljük [MO]L-el) egyszeruen
megadható az elemek sorbarendezésével.
Keressük tehát a M lexikografikusan K-adik permutációját P-t. Próbáljuk megkeresni Pl-et P első
betűjét, ekkor PI-re:

ipemünumcn       -1, r', f', [{MPI      }]L ) =< K,

ahol   f ,[;] .= {fU] - 1, ha j = Pl ,(; .=
            . .     .   "                       1,2, ...r)                                                   (1)
                   f[;],     egyébként

        r'      = {r :-1, ha f[PI]       =1
                   r , egyébkén!


és nyilván Pl az a szimbólum amelyre iperm2num(n-l,r',                   f',     [{MPI}]L)       maximális (1)
tulajdonságú.
Pl után P2-t mint            n-1 darab   és r' különböző     szimbólum         lexikografikus   sorrendben   vett
K - perm2num(n -1, r', f', {M Pl}) sorszámú permutációjának első betűjeként keressük ..
és így tovább egészen Pn-ig. Legyen az ezt megvalósító pascal kód megírása ismét az olvasó feladata!

P 1.2.1. Írjunk olyan pascal függvényt, amely az előzőek alapján generálja a K. sorszámhoz tartozó
lexikografikus permutációját valamely M halmaznak !

Valamely halmaz ismétléses permutációit          előállító algoritmust találunk még [Barti967]           -ben és
[SagI964]-ben is.

Kapcsolódó versenyfeladatok:

Feladat             Instrukció

UUSC85-2            Az összes esetek száma (kb. 9!) lehetővé teszi, hogy egyenként megvizsgáljuk őket
                    A megadott öt szám összes permutációit (5!) vizsgáljuk, az összes lehetséges
                    müveletjelezéssel (44).
TUBP91-4            Az egyenlő számjegyek elhagyása után az összes esetek száma 10!
RUGB92-1            Ismétléses permutációk lexikografikusan
RUGB92-4            Mivel a gráf csúcsainak száma nem több mint 8, ezért a csúcsok összes lehetséges
                    sorrendjét megvizsgálva (8!) a minimálisat bizonyosan megtaláljuk
ACMF92-2            A hálózatba kapcsolt gépek maximális száma 8, ezért az összes eset vizsgálata
KLTE93-3            lehetséges.


Irodalomjegyzék            az 1.2 fejezethez

 [Rohll978]:       J. S. Rohl. (1978). Generating permutations by choosing. The Computer Journal,
                   Vol 21., p 303.
 [we1ll971 ]:      M. B. Wells (1971). Elements ofCombinatorial Computing. Pergamon Press, New
                   York
 [Bratl967]:       P. Bratley (1967).Permutations with repetitions. CACM Vol. 10. p. 450
 [Sag1964]:        T. W. Sag (1964) Permutations of set with repetitions. CACM Vol. 7. p 585.




                                                                                                               12
1.3. Kombinációk

Ebben a fejezetben a feladatunk n-elem r-edosztályú ismétlés nélküli kombinációinak generálása,
úgy, a generált kombinációk sorrendje is számít. Ahogy már az ismétlés nélküli permutációkkal
foglalkozó fejezetben utaltunk rá, ez a probléma az ismétlés nélküli permutáció generálás
általánosításának tekinthető.
Most az előző fejezetekkel ellentétben csak a legáltalánosabb eljárást mutatjuk be. Nem foglalkozunk
a "rendezettlen" kombinációk generálásával. A lexikografikus algoritmusok közül is csak azzal
foglalkozunk, amely a lexikografikusan t. kombinációt fogja közvetlenül előállítani.
Keressük praktikusan a Z; = {l, 2, .... , n} halmaz r-edosztályú kombinációinak összes permutációit,
vagyis    a     Pn.r   = Pl>P2"'"
                              Pr ,ahol Pl>P2'" .,Pr E {l, 2, ... ,nl és Pi :j:; P, ha i :j:; j. vektorokat.
Ezek halmazát jelöljük P(n, r)-el. Nevezzük Pn. r = Pl> P2'"'' Pr -t a rövidség kedvéért el egy r-
permutációnak     !

Legyen P; r = Pl' P2"'"        Pr egy r-permutáció, ekkor Pn. r inverziója a Cn r = C1 , C2,···, Cr vektor,
ahol
Ci E {O, 1, .... ,n-i} és


                                                   r-i

                              Ci    = Pi   - i+   Lo          i, j
                                                   j=l
ahol

                              o = {O,hap.<p.             J'
                                   i.j      1, ha P, > Pi

Egy   Pn. r-hez tartozó Cn. r előállítása a fenti képIetet követve meglehetősen egyszerű, de
kihasználhatjuk a Pn.r és Cn.r kapcsolatának egy speciális tulajdonságát. Nevezetesen, hogy ha Z;
elemeit a már l.l-ben        megismert listában tároljuk, akkor c1-et úgy kapjuk, hogy megszámláljuk,
hogy ebben a listában hány elem előzi meg Pl -et, majd Pl -et töröljük a listából, c2 értékének
meghatározásához        az így nyert listában meg kell számlálnunk a P2 -t megelőző elemek számát, majd
 P2 -t is töröljük a listából a többi       Ci   -t ugyanilyen módszerrel kapjuk:

proeedure    eodingi
var i,t, eount:integeri
begini
Init;
for i:=l to r do
begin
 eount:=O;
 t:=O;
 while p[i)<>a[t)    do
 begin
   t:=a[t);
   ine(eount)
 end;
 eli] :=eounti
 a [t) :=a [a [t) )
end;

         Fig. 1.3.1.: Permutációk        inverziójánakgenerálása




                                                                                                          13
A   Cn, r ~   Pn, r   átalakítást végző eljárás szintén a fenti tulajdonságot használja ki:

procedure     Decoding;
var i,j,t:integer;
begin
 Init;
 for i:=l to r
 begin
  t:=O;
   for j :=1 to c[i] do t:=a[t];
  p [i] : =a [t] ;
  a [t] :=a [a [t] ];
 end
end;

         Fig. 1.3.2.: A C-fP átalakítást végző eljárás.

Meg kell még jegyeznünk, hogy lexikograftkusan kisebb r-permutációhoz lexikografikusan kisebb
inverzió fog tartozni, vagyis, hogy a Cn,rBPn.r megfeleltetés, ilyen értelemben rendezés tartó.
A lexikografikusan t. r-permutációt közvetlenül előállító rPermGen nevű eljárás egyegy-egy értelmű
megfeleltetés a Pen, r) és a Z           =
                                    {l,2, ...,IP(n, r)l} halmazok között. Konstruáljuk meg először
rPermGen inverzét, vagyis azt a RankrPerm nevű eljárást amely lexikografikus sorrendben
megsorszámozza Z; r-permutációit.
RankrPerm konstruálásához felhasználjuk a fent bevezetett inverziók és P(n,r) egy-egy értelmű
                                                                                                   =
kapcsolatát. Cn, r definíciójából látható, hogy egy cp cz, ... , c -vel kezdődő Cn. r CI' Cz, ... , Ci>"" Cr
                                                                           j


inverziót pontosan [Pm-i, r-i)1 olyan inverzió előz meg, amelyeknek c ,c Z , •.• ,c előtagjára az igaz,
                                                                                     I        l
                                                                                               j


hogy clj=cj     j=I,2, .. ,i-Iéscl <c    j  ugyanis, a c +l végigfut ja a [O,l, ..,n-(i+l)] intervallum
                                             j                    j


értékeit, C +Z pedig [O,1,..,n-(i+2)] intervallum on fut végig, és így tovább. Ekkor az így összeszámolt
               j


esetek száma:
(n-i)*(n-(i+l))*               ... *(n-(r-2))*(n-(r-l))=              (n-i)!   = IP(n-i,r-i)1            O
                                                                      (n-r)!
Vagyis a RankrPerm eljárás a következő lesz:

Procedure RankrPerm(c:codeword,   var Rank:integer);
var i:integer;
begin
 Rank:=l;
 for i:=l to r do Rank:=Rank+c[i]*P(n-i,    r-i);
end;

          Fig. 1.3.3.: A RankrPerm eljárás.

A hivatkozott P(n,k) függvény definíciója a pontosság kedvéért a következő:
                         nl
P(n,k)         :=         .
                      (n-k)!

A RankrPerm eljárás az adott r-permutáció inverziójához rendeli a kívánt sorszámot, vagyis egy
adott r-permutáció esetén RankrPerm hívását meg kell előzze a Coding eljárás hívása.

Az rPermGen eljáráshoz a tulajdonképpen már l.2-ben is alkalmazott trial-and-error módszer
alkalmazásával jutunk. A módszert most is az inverzfüggvényre (a RankrPerm eljárás) alkalmazzuk:




                                                                                                          14
procedure   rPermGen(rank, k:integer; var c:codeword);
var i:integer;
be gin
 for i:=n-k downto O do
   if rank > i*p (n-k, r-k) {Trial}
   then begin
     c[k):=i;
     if k <= r then RankrPerm(rank-i*P(n-k,   r-k),k+l);
     exit;
   end
end;

      Fig. 1.3.4.: Az rPermGen eljárás.

Azonos kiindulás után kissé eltérő gondolatmenetet találunk még [Knot1976]-ban   a problémára.   Az
eredeti problémához kapcsolódó feladatokat találunk még [Welll971] -ben.

Kapcsolódó versenyfeladatok:

Feladat          Instrukció
KLTE91-3
KLTE92-1         Lexikografikus kombinációk felsorolása


Irodalomjegyzék      az 1.3 fejezethez

[Knot197 6]:     G. D. Knott (1976). A Numbering System for Permutations ofCombinations.
                 Communications of ACM, Vol 19, p 355.
[We1ll971]:      M. B. Wells (1971). Elements of combinatarial programming. p 130.




                                                                                                  15
1.4. Egy halmaz összes részhalmazai, particionálása

Tegyük fel, hogy az a feladatunk, hogy egy halmaz (praktikusan            az {1,2, ... ,n} halmaz) összes
részhalmazait kell generálnunk lexikografikus sorrendben.
A fenti halmaz részhalmazainak reprezentációja legyen a következő:
Minden      egyes    részhalmazt   jelöljön   egy    f!.  = ~ ~ ...
                                                                 ar            számsorozat,       úgy,    hogy
~ < a2 < ... < ar'    ís r :s;n, ahol ~ ~ ... ar az adott részhalmaz elemi. Könnyen látható, hogy ez
a jelölés egyértelmű.
Definiáljuk most, az egy halmaz részhalmazainak halmazán értelmezett a lexikografikus rendezést:
Legyen f!.= ~ ~ ...         =
                      ap és f l1 <; ... cq két részhalmaz,
akkor
        ha létezik olyan i (l :s; i :s; q) amelyre minden 1 :s; j :s; i esetén      aj   =   cj

          és vagy ai < Ci
         (1)
             vagy p=i-1
        akkor azt mondjuk, hogy ~ lexikografikusan megelőzi (kisebb, mint) c-t.

Mindezek alapján pl. n=4 esetén a fenti halmaz részhalmazai lexikografikusan növekvő sorrendben a
következők: 1,12,123,1234,124,13,134,14,2,23,234,24,3,34,4

A fenti halmaz részhalmazait n-jegyű bináris számokkal is reprezentálhatjuk:          A   '1 b2 ••• bn pontosan
akkor tartozik az A részhalmazhoz, ha bi   = 1 <=>   i E A (i = 1,2, .. , n)

Vegyük észre, hogya részhalmazokon értelmezett lexikografikus rendezés nem ugyanazt a sorrendet
adja, mint az őket reprezentáló bináris számokon (bit-sztringeken) értelmezett lexikografikus
rendezés.

Ha valamely feladat a részhalmazok felsorolásán kívül, nem követeli meg tőlünk a lexikografikus
rendezettséget,      akkor   az    {1,2, ...,n} halmaz      részhalmazainak     felsorolása  bináris
reprezentációjukban, nem más, mint az egész számok bináris alakjainak felsorolása. O-től 2n-l-ig.
Egy szám bináris alakjának keresésekor kihasználhatjuk, hogy a Pascal a Word típus esetén a
számokat éppen a nekünk megfelelő formában tárolja.
Egy kicsit bonyolultabb probléma az (l)-el defmiált rendezés szerint a részhalmazok felsorolása, ezt
valósítja meg a következő program:

Program Subset_enumeration;
Const n=13;
{ <n> elemű halmaz részhalmazait    soroljuk föl lexikografikusan
  A program ebben a sorrendben bármely t.-ediket képes generálni
   (két részhalmaz  akkor különbözik, ha van különböző elemük)
  A teljesítményről:    486SX25-on n=13 esetén az összes részhalmaz
  felsorolása   kb 40 másodpercet  vesz igenybe

var
 t:longint;
 i,r:integer;
 b:array[l ..n] of integer; {ha az b[i]>O akkor az i-edik elem a
                             reszhalmazban  van}
 a:array[l ..n] of integer; {az elso r eleme nem mas, mint a t.
                             reszhalmaz   }

  function kettoad(x:integer) :longinti
  var c:longint;i:integer;
  be gin  c:=l; for i:=l to x do c:=c*2;                        Kettoad:=c           end;

                                                                                                              16
procedure  subset(t:integerivar                                      r:integer)i
var
 k:integerih:longinti
begin
 for i:=l to n do b[i] :=Oi
    r:=Oi
    k:=li
  repeat
   h:=kettoad(n-k)i
   if t<= h then begin b[k] :=li                                     inc(r)i      arr] :=k endi
   t:=t-(l-b[k])*h-b[k]i
   inc (k)
  until ((k>n) or (t=O))
 endi

begin
 for t:=l to kettoad(n) do
 begin
   subset(t,r)i
  writelniwrite(t,'  :')ifor                                  i:=l to r do write(a[i],'                     'li
 end
end.

        Fig. 1.4.1.: Az {1,2, .... ,n} halmaz osszes részhalmazának felsorolása.

A fenti program az eredeti célkitűzést általánosítva alkalmas arra, hogy az {1,2, ... ,n} halmaz az (1)
szerinti rendezésben t-edik részhalmazát generálja. A program subset eljárása hívás után az a, b
globális változóban t-edik részhalmazt, az r-paraméterében pedig a t.-részhalmaz elemszámát adja
vissza.

Legyen         Z;     = {I, 2, ....     , n}.   Ekkor    a      Z;    halmaz     egy     partíciója   az    azon   halmazok
P   = {7rl'   7r2, ••• , 7rm}     halmaza, amelyre
                        7ri    n 7rj = 0,   ha i'1':. j
                 és      7r '1':.0, i=I,2, ... ,m
                              1


              (2 )
                         m
                 és      Unt
                         i=l
                                      = Z;
feladatunk rögzített n esetén az összes fenti tulajdonságú halmazrendszer előállítása.

Számítsuk először ki, hányféle különböző particionálása létezik az {1,2, ... ,n} halmaznak !

Jelölje Bn a keresett partíciók számát (Bell-féle szám), ekkor n= 1 esetén nyilván B 1= 1
Tegyük fel, hogy Bl, B2, ••• ,Bn_l ismert. Ekkor Hn_l-hez vegyünk egy a többitől különböző elemet,
jelöljük ezt a-val. Keressük meg H;                     = Hn_
                                             {a} összes partícióit !
                                                              l U

Világos, hogy azon partíció száma, amelyekben {a} szerepel B n-l' hiszen ezeket úgy nyerhetjük,
hogy Hn_l-minden partíciójához hozzávesszük a {a} halmazt. Továbbá az {a, p}-t (p EHn_l)
tartalmazó        partíciók           száma, egy rögzített p -esetén           Bn_2, mivel p-t összesen (n~l)-féleképpen
választhat juk ki, ezért azon partíciók                      száma, amelyekben         ex, egy kételemű    részhalmazba   esik




                                                                                                                            17
( n~l) . Bn_l' és ugyanígy gondolkozva azoknak a partícióknak a száma, ahol                   a. egy i-elemű

részhalmazba esik (~~:). Bn_i
Vagyis H;    = Hn-   1   u {a} összes partícióinak száma:


B n = n~.
      1=1
            (n-l)
             1-
                             n    (n-l)        n-I
                  1 B n-m = 1=1 n - 1. B n-l.= k=O
                            L                   L
                                                     (n-l)
                                                       k     Bk


A táblázat Bn értékeit mutatja:

                                                                                               12
                                                                                            4213597

      Fig. 1.4.2.:   s; értékei
A következőkben M. C. Er 1987-ben publikált [MCER1987] módszerét mutatjuk be.

Legyen a C    = llC2 ••• cn kódszó a Z; egy partícióját    leíró kódszava, úgy, hogy




n=4 esetén a következő táblázat mutatja a kódszavakat:

                                          Kódszó
                                           1111
                                           1112
                                           1121
                                           1122
                                           1123
                                           1211
                                           1212
                                           1213
                                           1221
                                           1222
                                           1223
                                           1231
                                           1232
                                           1233
                                           1234

       Fig. 1.4.3.: Az {1,2,3,4} halmaz összes partíciói és a hozzájuk tartozó kódszavak.

A táblázatból is látható, hogy ebben a kódszó konstrukcióban 1 ~ Ci ~ i. Másszóval i E Z; nem
kerülhet 1ttbe, ha j > i. A következőkben azt vizsgáljuk, hogy a Z; halmaz összes partícióinak
kódszavai    hogyan vezethetők le a Zn_l           halmaz összes partícióit   leíró kódszavak     sorozatából
hozzáadva cn -t a már meglévő kódszavakhoz. cn értékei az 1,2 ... ,max( ll, C;... Cn-1) + 1 intervallum
értékei közül kerülnek ki.
Ezen tulajdonságok segítségével működik 4.4 algoritmusunk, amely a megfelelő kódszókat generálja.
A hivatkozott SP(m, p) eljárás definíciójában az m = max( eJ, ~ ... Cn_l)     és p paraméter adja az
aktuális kódszó aktuális jegyét. Eljárásunk feltételezi a PrintPartition eljárást, amely a kódszó B
 partíció konvertálást ill. az így nyert partíció megjelenítését végzi. Mindezek után eljárásunk a
következő:

                                                                                                           18
type   codeword=array[l                   ..max]           of integeri

procedure    SetPartitions(n:integer)i
var c:codewordi
procedure    SP(m,p:integer)j
var i:integeri
 begin
   if p > n then PrintPartition(c)
   else
    begin
     for i:=l to m do
     begin
       c[p] :=iiSP(m, p+1) i
     endi
     c[p] :=m+1i SP(m+1, p+1)
    end
  endi
begin
  SP(O, 1)
endi

       Fig. 1.4.4.: M C. Er rekurzív algoritmus az {l.2 ....•n} halmaz összes partíciójának generálásához.

A fenti Pascal kód Borland Pascal 7.0-ás verziójú fordítót használva napjaink átlagosnak mondható
teljesítményű PC-jén (486 SX 25) a következő futási időeredményeket adta:

          n                         Futási idő
          10                        < 1 sec
          11                        < 4 sec
          12                        < 21 sec
          13                        < 130 sec

       Fig. 1.4.5.: Az Er-féle rekurzív halmaz particionáló algoritmus futási ideje néhány n-re.

Mint a táblázatból is látható, a versenyfeladatok esetén akkor alkalmazható az algoritmus ha
feldatbeli n < 13.
Ha az eredeti problémát, úgy módosít juk, hogy csupán a lexikografikus sorrendben t. partíciót
keressük, akkor a fenti Er-féle algoritmus nem használható hatékonyan, hiszen ahhoz, hogy
megkapjuk a a t. partíciót le kell generáltatnunk a megelőző t-l darab partíciót is.
A lexikografikusan t. partíciót előállító algoritmust megkaphatjuk, ha megfeleltetést találunk a
természetes számok és a már ismertetett konstrukcióval előállított kódszavak között.

Az említett megfeleltetés bemutatásához vezessük be a következő jelöléseket:

Dn(r, d) :azon    CIC2,,,,Cn    kódszavak                száma,
                  ahol CIC2,,,,Cr         rögzített        és
                  d   = max(c ,cl    2,    •.   ,cr_l)            (r=2, ... ,n+1; d=1, .. ,r-1)

Könnyű látni, hogy mivel minden kódszóra igaz, hogy CI = 1 ezért D; (2, 1) = En, továbbá az is igaz,
hogy Dn(n+l,d)=l               (d=l,            2, ... n) ,valamint Dn(n,d)=d+l     (d=l, ... ,n-l).

Határozzuk meg D; (n -1, h) -t!




                                                                                                             19
Ekkor

        Cl ,C2,···   ,cn-2 ,Cn-l ,Cn
        '----v------'
               max=h

        (1)

vagyis cn_l helyére 1,2,,,.,h, h+ 1 kerülhet, ezek közül 1,2,,,.,h úgy, hogy maxfc, ,c2' oo,cn_l) = h
igaz marad, vagyis az ilyen kódszavak száma h* Dn(n, h),
ha cn-l = h + 1 (azaz cn_l-et is rögzítjük) , akkor az ilyen ( 1) tulajdonságú kódszavak száma
Dn (n, h + 1), vagyis összesen:

                                                                                                 (2)

Hasonló okoskodással, kapjuk, hogy

Dn(r, d) = d * Dn(r + 1, d) + Dn(r + 1,d + 1)        (r = 3,oo,n -1; d = l,oo.,r -1)       (3)
Vagyis D; (r, d)értékei könnyen kiszámíthatók. Ezek alapján a sorszám ~ kódszó konverzíót végző
algoritmust könnyű elkészíteni:

procedure  rank(t, n:integer; c:codeword)i
var r:integer;
begin
 t:=l; d:=l;
 for r:=2 to n
  begin
    do t:=t+([r]-1)*Dn[r+1,  d];
    if c[r]> d then d:=c[r]
   end
end;

Az eljárás feltételezi, hogya Dn[2"n+ 1, 2"n+ 1] tömbben e D; (r, d) megfelelő értékei vannak.
Az inverz eljáráshoz, vagyis a kódszó ~ sorszám konverzióhoz, hasonlóan gondolkodva,      mint az l.2
fejezetben a következő algoritmust kapjuk:

program Set_Partitionsi
const
 Bell:array     [0..15] of longint=
        (1, 1, 2, 5, 15,
         52, 203, 877, 4140, 21147,
         115975, 678570, 4213597, 27644437,                      190899322,
         1382958545   )i
 var
   n:integerit:longint;r,i,j:bytei
   Dn:array[1 ..18,1 ..18] of longinti
   C:codewordi

  procedure   PreCalcD(n:integer); { A D(n) értékek kiszámítása}
  var d,r:integer;
  begin
   Dn [2,1] :=BELL [n] ;
   for d:=l to n-1 do Dn[n,d] :=d+1i
   for d:=l to n do Dn[n+1,d] :=1;
   for r:=n-1 downto 3 do
     for d:=r-1 downto 1 do Dn[r,d]:= d*Dn[r+1,d]  + Dn[r+1,d+1]i

                                                                                                       20
endi


Procedure partition(tO:longint                        var d:byte)i
var r:integeri
    t,m,k:longinti
Begin
 t:=tO id: =1 i
 for r:=2 to n do
  begin
     m r=O)
      repeat inc(m) until                     t <= m * Dn[r+1,d])i
      if d+1 < m then m:=d+1i
      c [r] : =mr
    t:=t-(m-1)*Dn[r+1,d]i
     if d < m then d:=mi
   endi
 endi

 begin
  writelni write(' n:')i readln(n)i
  PreCalcD(n)i c[l] :=li
  for t:=l to BELL[n] do {a ciklus az összes partíciót generálja}
  begin
   partition(t,r)i {a t. partíció elő állítása}
   for j:=l to r do
    begin
     write(j,'. reszhalmaz elemei:')i
     for i:=l to n do if c[i]=j then write(i, " ')i
     writeln
       e nd r
  end
 end.

A programpartition(t, r) eljárása generálja a lexikografikusan t. kódszót. Az eljárás a kész kódszót a
már fent definiált codeword típusú C globális változóba teszi, míg a kódszóban leírt partíció
részhalmazainak számát az r-paraméterében adja vissza. A kódszó partícióvá alakítását a fóprogram
végzi. A fenti program egy adott n-hez tartozó {1,2, ...,n} halmaz összes partícióját előállítja úgy,
hogy végrehajtja a partition(t, r) eljárást a t= 1,2,3, .... ,Bn értékekre.
Az összes partíció generálását új algoritmusunk valamivellassabban            végzi, viszont cserében a t.
partíciót közvetlenül, az ezt megelőző t-I partíció előállítása nélkül állítja elő.

A témakörhöz a következő feladat kapcsolódik:

KLTE92-7
A probléma neve: Szállítás
Mivel a probléma csupán az n<100 korlátozást tartalmazza, ezért az összes eset generálása és
ellenőrzése a fenti módszerek bármelyikévei is, nem valószínű, hogy megoldáshoz vezet akkor ha a
zsúri teszt inputadataiban a csomagok száma több, mint 13. Az előző korlátozás bevezetésévei 4.4
algoritmusunkkal a csomagok halmazának összes partícióinak előállításával és ellenőrzésévei
kiválaszthatjuk az optimálist.
Az eredeti feladat megoldásához más módszert kell keresnünk !
További kapcsolódó versenyfeladat: RUGB91-6




                                                                                                        21
Irodalomjegyzék   az 1.4 fejezethez


[MCER1987]:   M.C. Er (1987) Alghorithm for generating Set Partitions. The Computer Journal
              Vol. 31 No 3, pp 283




                                                                                              22
1.5. Gray kódok

A Gray kódok olyan k-bites bitminták sorozata, amelyek olyan tulajdonságúak, hogy az egymást
követőek egymástól egyetlen bitben különböznek. Például a következő 3-bites Gray kódok
alkalmasak arra, hogy a O-tói 7-ig a számokat kódolják:

           Gray kódok              Sorszám           Decimális
                                                       érték
                000                       O               O
                100                       1               4
                101                       2               5
                001                       3               1
                011                       4                3
                010                       5                2
                110                       6                6
                111                       7                7

      Fig. 1.5.1.: Gray kódok

A Gray kódok között speciális tulajdonságúak a Ciklikus Gray kódok, amelyekre a fentieken kívül az
is igaz, hogy az utolsó és az első is egyetlen bitben különbözik egymástól. A fentiek nem Ciklikus
Gray kódok. A továbbiakban csak Ciklikus Gray kódokkal foglalkozunk, ezért a rövidség miatt csak
Gray kódokként hivatkozunk rájuk.

            Gray kódok              Sorszám          Decimális
                                                       érték
                 000                      O                O
                 001                      1                1
                 011                      2                3
                 010                      3                2
                 110                      4                6
                 111                      5                7
                 101                      6                5
                 100                      7                4


       Fig. 1.5.2.: Ciklikus Gray kódok

Itt megjegyezzük, hogy a Gray kódok O-val (minden bit O) kezdődnek és a következő sza.bállyal
generálhatóak: Minden egyes lépésben az adott Gray kódból egyetlen bit negálásával apjuk a
következő Gray kódot. Meg kell határoznunk ennek a bitnek a pozícióját. Erre a pozícióra pedig az
igaz, hogy pontosan fele annyiszor kel1 változtatni, mint a tőle közvetlenül jobbra ál1ót. Ez a módszer
viszonylag könnyen programozható, de megvan az a hátránya, hogyan-edik              Gray kódot csak a
megelőző n-l kód legyártása után adja. Ismét két problémát fogalmazhatunk meg.:

 (a) Soroljuk fel az összes k-bites Gray kódot
 (b) Írjuk fel ak-bites Gray kódok közül az n. et.

 Természetesen, a (b) probléma az általánosabb. De néha, ha csupán az (a) -problémát kell
 megoldanunk akkor ha az (a)-t megoldó algoritmusunk egyszerűbb és gyorsabb, mint az
 általánosabb (b)-t megoldó algoritmusunk, akkor érdemesebb azt használni (lásd például permutáció
 generálás esetét).
 Keressünk a fent már leírt módszemél egyszerűbb et az (a) problémához, valamint minél egyszerűbb
 megoldást a (b)-problémához!
 Jelöljük G(k) = (go. gp ...• g2k_) -val a k bites Gray kódokat.




                                                                                                     23
Ekkor                        G-t                              a                következő                     rekurzióval
defmiálhatjuk:
 G(1) = (0,1)
{ G(n + 1) = (Ogo,Ogpo .. ,Og2n_l'Ig _, , ... , Ig )                                                               (1)
                                    2n            o

Vagyis ezzel egyben módszert adtunk az (a) probléma megoldásához.                           Az (1) képletből könnyen
megkap hatjuk az m-edik Gray kód n. bitjét:


g(m, n)   = (mmOd2"
               2n-                <1
                                       )
                                                                                                                   ( 2)
                      '
Vagyis a (b) problémát akár egy, a (2) formulát használó algoritmussal is megoldhat juk,

Legyen
                    D(n)    = (ct.,~,... ,d2n),               úgy, hogy di   gi hányadik biten tér el gi-l -től.

akkor

 D(1)     =1
{ D(n+ 1) = (D(n),n+                                                                                               (3)
                                       1,D,ev(n)),     ahol D,ev(n):= D(n) jordítottja

Ha

és
                                                         n
          g;   = (bn,bn_  p •••   ,'1), hogy i = 'Llj2j                                                            (4)
                                                 j=O
akkor


               b. =/. xor J-
                J   J
                           l,      I        j   = 1,2, ....   ,n

Ez utóbbi formula teremt kapcsolatot egy szám bináris alakja és a hozzá tartozó Gray kód között.Az
ezen a formulán alapuló algoritmusokat megvalósító program oknak megfelelő típusválasztás esetén
(Pascal esetén a Word típus ilyen) nem kell tartalmazniuk bináris számmá alakító eljárást, hiszen azt
a számítógép változó-értékadáskor elvégzi. Sőt ha észreveszzük, hogya bj-t előállító sor egy bináris
l-bittel jobbratolást fed, akkor a programunk a szám bináris alakjából egyetlen utasítással
előállíthatja a megfelelő Gray kódot bitmintáját. Mivel a feladatok között találtam olyat, amely
megoldásához az (a) ill. (b) problémát is meg kell oldani, ezért a Gray kód generáló algoritmusunkat
ebbe ágyazva közöljük:

USEC92-1
A probléma neve Bit Twiddler:
Származása: 1992 ACM East Central Regional Programming Contest

A probléma lényege k-biten (k<=15 ) generálni a Gray kódokat az n.-től az m.-ig, n, m, k a program
inputjai. A program outputja a megfelelő Gray kódok decimális alakja:A problémát két
részproblémára lehet bontani:

          (1) k-biten az n-edik Gray kód generálása
          (2) valamely k-bites Gray kódból kiindulva a következő m darab k-bites Gray kód generálása

 Mivel a feladat k-ra vonatkozó korlátja (k<=15) elegendően kicsi, ezért használhat juk a már fent
 emIített pascalbeli Word típu st, valamint a memóriában elfér az összes l5-bites Gray-kód, amely

                                                                                                                          24
tartalmazza (l)-miatt az összes 14, 13, 12, ..,2, l-bites Gray kódot. Az init eljárás generálja a 15-
bites    Gray kódokat, ahogy ígértük egyetlen értékadással. A program többi eljárása a
követelményeknek megfelelő outputot (az adott Gray kód decimális alakját) állítja elő.

program Twiddler;
uses ert;
type gray = array[O ..O] of word; {Csak a cím miatt}
var    g: Agray;
  b,j,t,i: word;

 proeedure init;
{ A gray kódok előállítása}
 var i:word;
 begin
  for i:=O to maxint do gA[i] := (i xor (i shr 1));
 end;

  proeedure put(tol,ig,biten:word);
  var i:word;
  begin
   for i:=tol to ig do writeln(gA[i]);
  end;

be gin
 elrseri
 getmem(g,65535);
 init;
 repeat
   write('Mettől?  ');readln(t);
   write('Meddig?  ');readln(i);
   write('Hány biten?   ')ireadln(b);
   if b=O then halt(O);
   put(t,i,b-1)
 until false
end.

       Fig. 1.5.3.: Twiddler.pas




                                                                                                   25
2. Geometriai algoritmusok

Bevezetés

A számítógépeket egyre többen és egyre gyakrabban használják olyan alapvetően geometriai eredetű,
nagy mennyiségű adat feldolgozásával kapcsolatos problémák megoldására, mint az alakfelismerés, a
térinformatikai,    térképészeti vagy háromdimenziós      szimulációs problémák.       A geometriai
algoritmusok szintén nagyon fontosak az olyan komplex fizikai rendszerek tervezésében és a
elemzésében, mint például az épületek, autók, gépek és integráltáramkörök.                  Az ilyen
programrendszerekben       a tervezők a fizikai objektumoknak megfeleitetett gépi objektumokkal
dolgoznak. Ezen gépi objektumok megfelelő szintű számítógépes kezelése, megjelenítése igazán
komoly feladat.
Az ilyen alkalmazások olyan alapvető objektumai, mint a pontok, szakaszok vagy poligonok és a
hozzájuk kapcsolódó alapvető eljárások adják az általában a nehezebbek közé sorolható geometriai
versenyfeladatok megoldásának alapjait.
A geometriai problémákat könnyen vizualizálhatók, de ez néha nem könnyíti meg a megoldás
keresését, inkább csak a probléma megértésében segítenek. Nagyon sok probléma megoldása, amely
 "szabadkézzel" pillanatok alatt megoldható egy darab papír és ceruza segítségével (pl. eldönteni,
 hogy egy pont egy poligon belsejében van -e vagy sem) követel egyáltalán nem triviális számítógépes
 programot.
Néhány geometriai versenyfeladat megoldásához elegendő a középiskolai koordinátageometriából
 tanult alapvető függvények, eljárások (pl. pontok távolsága a síkon, egyenes és pont távolsága,
 háromszög területe) pontos (le)programozása. Ezek megoldása inkább a szimulációs problémák
 megoldásához hasonlít.
 További "geometriai" problémák megoldását tisztán vagy nagyrészt kombinatorikai algoritmusok
 adják, ezért az ilyen problémák elemzése előtt érdemes a "Kombinatorikai algoritmusok" című
 fejezetet (még egyszer) áttanulmányozni.
 A geometriai tartalmú versenyfeladatok harmadik csoportja olyan problémákból áll, amelyek
 megoldásához az első csoportbeli alapvető geometriai függvények pontos megvalósítása, valamint
 kombinatorikai alapismeretek szükségesek.
 A legtöbb algoritmus, amit tanulmányozni fogunk a legegyszerűbb geometriai objektumokkal fog
 dolgozni a két dimenziós véges, de megfelelően kiterjedt síkon. A legalapvetőbb geometriai
 objektumot a pontot egy számpárral fogjuk jellemezni (a pont 'koordinátái' a derékszögú koordináta
  rendszerben). A szakasz reprezentációja egy pontpár, amelyeket az adott szakasz összeköt. A poligon
  esetünkben pontok listája, és feltételezzük, hogy az egymásután következő pontok szakaszokkal
  vannak összekötve, valamint, hogy az utolsó pontot az elsővel szintén egy szakasz köti össze, így
  alkotva egy zárt alakzatot.
  Az egységesség és az egyszerűség kedvéért rögzítsük le most, hogy hogyan fogjuk ezeket az
  objektumokat a programjainkban reprezentálni.
  A poligonok reprezentációja egy tömb. Használható lenne még láncolt lista is, de a listák alapvető
  műveleteinek programozása a pascalban egy kicsit sok adminisztrációt követel meg a programtói,
  ezért a tömbök használata a programokat egyszerűbbé teszi. Ha valamely probléma pontoknak egy
  halmazát érinti, akkor a reprezentációban szintén az array[O ..max] of pont definíciót fogjuk
  alkalmazni, ahol max valamilyen elegendően nagy szám.

Tehát programjaink a következő reprezentációkat fogják használni:

 type pont       =    record
                      x :integeri
                      y: integer
                     endi

 type   szakasz       =     record
                             pl:ponti
                             p2:pont

                                                                                                   26
endi

var poligon:         array{O ..max]         of ponti

      Fig. 2. O.J.: Geometriai objektumok reprezentáció ja.

Furcsának tűnhet, hogy a pontok a koordinátarendszer rácspontjaira korlátozódnak. A valós
reprezentáció ugyanígy használható lenne, de az egész értékeket használata sokkal átláthatóbb és
hatékonyabb (az egész számok műveleteit a számítógép sokkal gyorsabban végzi, mint a
lebegőpontosakat) programot eredményez, nem beszélve arról, hogy a versenyfeladatok általában
megelégszenek az egész értékű geometriai programozással is.




                                                                                              27
2.1. Szakaszok metszete

Az első probléma melyet tárgyalni fogunk az egyik legalapvetőbb probléma ezért számos más
algoritmus is hivatkozni fog rá, ezért nagyon fontos a precíz kidolgozása .. A probléma a következő:
Adott két szakasz, amelyek ponttá is fajulhatnak (kezdő és végpontjuk egybeesik) eldöntendő, hogy
metszik-e egymást vagy sem. A következő ábra néhány lehetséges előfordulást mutat:




                                                                                    f·
      Fig. 2.1.1.: Szakaszok a síkon.

A kézenfekvő algoritmus esetünkben az, hogy számítsuk ki annak a két egyenesnek a metszéspontját,
amelyek tartalmazzák     a kérdéses szakaszokat, majd vizsgáljuk meg, hogy a szakaszaink
tartalmazzák-e ezt a metszéspontot. Amennyiben a kérdéses egyenesek párhuzamosak, akkor azt kell
vizsgálnunk, hogy valamely szakasz végpontja a másik szakaszra esik-e. Az egyenesek
metszéspont jának kiszámítása visszavezethető egy kétismeretlenes egyenletrendszer megoldására,
amely útmutatást pl. [BELT1989]-ben találhatunk.

  function metszet (pO,pl,p2,p3: pont) :booleanj
  var
    nev,a,b,c,d,x,y:reali
    flagl,flag2,flag3,flag4:booleanj
  begin
   a:=pl.y-pO.Yi b:=pO.x-pl.x;
   c:=p3.y-p2.Yi d:=p2.x-p3.Xi

   nev:=a*d-b*ci           {az egyenletrendszer matrixanak determinansa}

   if (abs(a)+abs(b»O) and (abs(c)+abs(d»O)
      {ha egyik szakasz sem pont}
   then
     if nev<>O {nem parhuzamosak}
     then begin
        { (x,y) a tartalmazo egyenesek metszespontja }
        y:=(a*(c*p2.x+d*p2.y)-c*(a*pO.x+b*pO.y))/nevi
        x:=(d*(a*pO.x+b*pO.y)-b*(c*p2.x+d*p2.y))/nevi

          flagl:=((pO.x<=x) and (x<=pl.x)) or
                  ((pl.x<=x) and (x<=pO.x));
          flag2:=((p2.x<=x) and (x<=p3.x)) or
                  ((p3.x<=x) and (x<=p2.x))j
          flag3:=((pO.y<=y) and (y<=pl.y)) or
                  ((pl.y<=y) and (y<=pO.Y))j
          flag4:=((p2.y<=y) and (y<=p3.y)) or
                  ((p3.y<=y) and (y<=p2.Y))j
          metszet:=flagl and flag2 and flag3 and flag4

       end
       else {a szakaszok parhuzamosak}

                                                                                                   28
if a*p2.x+b*p2.y=a*pO.x+b*pO.y
      then begin
       flagl:=(((   (pO.x<=p2.x) and (p2.x<=pl.x)   ) or
               ( (pl.x<=p2.x) and (p2.x<=pO.x)    ) ));

        flag2:=(((  (pO.x<=p3.x) and (p3.x<=pl.x)   ) or
                ( (pl.x<=p3.x) and (p3.x<=pO.x)   ) ));

        flag3:=(((  (p2.x<=pO.x) and (pO.x<=p3.x)   ) or
                ( (p3.x<=pO.x) and (pO.x<=p2.x)   ) ));

        flag4:=(((  (p2.x<=pl.x) and (pl.x<=p3.x)   ) or
                ( (p3.x<=pl.x) and (pl.x<=p3.x)   ) ));

        metszet:=   flagl or flag2 or flag3 or flag4;
       end
       else
        metszet:=false
  else {valamelyik szakasz pont l}
   if ((abs(a)+abs(b))>O)     and (abs(c)+abs(d)=O)
       {a masodik szakasz pont}
   then
    if a*p2.x+b*p2.y=a*pO.x+b*pO.y
    then
      metszet:=((pO.x<=p2.x)     and (p2.x<=pl.x)) or
                  ((pl.x<=p2.x) and (p2.x<=pO.x))
    else
      metszet:=false
   else
     if (abs(a)+abs(b)=O)    and    (abs(c)+abs(d»O)
          {az elso szakasz pont}
     then
      if c*pl.x+d*pl.y=c*p2.x+d*p2.y
      then metszet:=((p2.x<=pl.x)      and (pl.x<=p3.x)) or
                        ((p3.x<=pl.x) and (pl.x<=p2.x))
      else metszet:=false
     else {mindketto pont}
      metszet:=(p2.x=p3.x)    and (pO.y=p2.y)
 end;

      Fig. 2.1.2.,' Szakaszok metszetén ek vizsgálata.

Sajnos a túl sok eset vizsgálata és kezelése eléggé bonyolult pascal kódot eredményez. A
versenyfeladatok megoldásához nem mindig szükséges a fentihez hasonló, általános algoritmus (pl.
ha a szakaszok nem fajulnak pontokká, akkor az algoritmus és vele együtt a kód is lényegesen
egyszerűbb). A fuggvény alkalmazhatóságát megnehezíti, hogy a kód nem eléggé átlátható és ezzel
együtt nehezen debug-olható,
Az algoritmust megvalósító fuggvényt könnyedén át lehet alakítani úgy, hogy amennyiben van
metszéspont annak koordinátáit adja is vissza, hiszen pl. nem elfajuló esetben a fuggvény x,y
változójában a metszéspont koordinátái előállnak.
A fentinél talán egyszerűbb és szintén általános algoritmust mutat be R. Sedgewick [SEDG1988]
művében. A közölt algoritmus nem számítja ki explicit módon a szakaszokat tartalmazó egyenesek
metszéspontját, hanem a szakaszvégpontok elhelyezkedése alapján dönti el, hogy van-e metszéspont.
Sedgewick közli az algoritmus pascal kódját is, de az sajnos nem működik. Viszont az algoritmusnak
megvan az az előnye a fentivel szemben, hogy a (helyes) pascal kódja talán kevesebb sorból állna.


                                                                                                29
Kapcsolódó problémák:

P2.2.1. Adott n szakasz, eldöntendö, hogy zárt poligont alkotnak-e.
P2.2.2. Adott n szakasz, eldöntendö, hogy páronként metszik-e egymást.
P2.2.3. Hány metszéspont ja van egy adott n csúcsú konvex poligon átlóinak.




Irodalomjegyzék     a 2.1.1. fejezethez.


[SEDG 1988]:    Robert Sedgewick: Algorithms. Second Edition. 1988. p. 349.
[BELTI989]:     Bélteky Károly: Analitikus Geometria és Lineáris Algebra. 1989.




                                                                                  30
2.2. Poligon és pont

A versenyeken gyakran tűnnek fel olyan feladatok, melyek arra az alapproblémára vezethetők vissza,
amelyben az algoritmusnak el kell döntenie, hogy egy adott pont egy adott (konvex vagy konkáv)
poligon belsejében van-e. A kérdést 'emberi' intelligenciával, szabadkézzel meglehetősen egyszerű
eldönteni, viszont a gépi megoldás már nem triviális.
Az egyik legegyszerűbb algoritmus a következő:

      l. Ellenőrizzük, hogy a vizsgálandó pont nem esik-e a poligon valamely          csúcsára   vagy
        oldalára. Ha igen akkor a pont az adott poligon belső pontja.

      2. Keressünk egy olyan pontot a síkon, amely biztosan az adott poligonon kívül van. Mivel a
        poligont véges sok pont feszíti ki, ezért valamely irányban egy elegendően távoli pont
        biztosan a poligonon kívülre esik. Legyen ez a pont Q

      3. A vizsgálandó pontból (P) húzzunk egy egyenest Q-ba.

      4. Vizsgáljuk meg, hogy a PQ egyenesnek van-e közös pontja a poligon csúcsaival, ha igen
        keressünk egy a mostani Q-tól különböző új külső pontot-o 2. pont

      5. Számláljuk meg a PQ egyenes és a poligon oldalainak metszéspontjainak számát. Ha ez
        páros akkor Papoligonon kívül volt, páratlan esetben P a poligon belső pontja.

A 4.pontbeli feltétel az ábrán is látható kivételek miatt kell ellenőriznünk. A programunkban     egy
adott P-hez a megfelelő Q-t a következő módszerrel keressük meg:

      4.1. Megkeressük azt a poligon[i] csúcsot, amely a poligon legjobboldalibb csúcsa

      4.2.q.x:=       poligon[i]         .x+1,    q.y:=poligon[i].y

      4.3. Ellenőrizzük, hogy Q megfelel-e a fentieknek, ha nem q. y: =q. y+ 1, majd .....-;3.3.pont.

Mivel a poligon véges sok csúcsból áll, ezért a 4.3 ciklus véges sokszor (legfeljebb n-szer, ha n a
csúcsok száma) fog végrehajtódni, véges sok lépésben megtaláljuk a megfelelő Q pontot.




                                                                                                   Q


                                     Q
                                                   p
                                                       ------..---,"




       Fig. 2.2. J.: Pont és Poligon elhelyezkedései    a síkon

 A fenti algoritmus egy lehetséges megvalósítását mutatja a következő program:




                                                                                                    31
program insidei
uses crti
const max=20i

type   pont=record   x,y:integer     endi

var poligon: array[O ..max]        of ponti
    p,q:ponti
    count,i,n:integeri
    ctrl, ctr12:booleani

procedure   InputPoligoni
var i:integeri
begin
 clrscri
 write('n   :')ireadln(n)i
 writeln('-------------------------------------------')i
 for i:=l to n do
 begin
  writeln('Az    " i , '-edik csucs koordinatai')i
  write('x:    ')ireadln(poligon[i]  .X)i
  write('y:    ')ireadln(poligon[i]  .y)i
 endi
 poligon[O] :=poligon[n]i
endi

procedure   InputPonti
begin
 writeln('A   pont koordinatai')i
 write('x:   ')ireadln(p.x)i  write('y:       ')ireadln(p.y)i
endi

begin
 InputPoligoni
 InputPonti

 ctr12:=falsei    i:=Oi
 repeat
   ctr12:=ctr12    or metszet2(p,p,poligon[i],poligon[i+l])i
   .i nc t í.j r
 until (ctr12) or (i=n-l)i
 if ctr12 then writeln('*8enne')
 else be gin
  q.x:=-maxinti
  for i:=l to n do
  begin
   if poligon[i] .x > q.x
   then
      begin q.x:=poligon[i] .x+li q.y:=poligon[i].y      endi
   endi
  repeat
    q.y:=q.y+li
    ctrl:=falsei
    for i:=l to n do ctrl:=ctrl or
                        metszet(poligon[i],poligon[i],p,q)i
  until not ctrli

                                                                32
if (p.x = q.x) and (p.y   q.y) then writeln('*Kivul')
  else begin
   count:=O;
   for i:=O to n-l do
    if metszet(poligon[i], poligon[i+l], p,q) then inc(count);
   if odd(count) then writeln('*Benne ') else writeln('*Kivul ');
  end
 end;
 end.

      Fig. 2.2.2.,' A fenti program eldönti, hogy egy adott pont egy adott poligon belsejében van e.

Az algoritmus egyetlen feltételezést támaszt az input adatokkal szemben, feltételezi, hogy azok a
megadott sorrendben egy zárt, önmagát nem metsző alakzat valamilyen rögzített körbejárás szerinti
csúcsai. Az alábbi poligonokat algoritmusunk inputjaként meg lehet úgy adni, hogy kielégítsék e
feltételt, ezért ezek az esetek is kezelhetők:




Sedgewick az előző problémánál hivatkozott könyvében erre a problémára is ad megoldást.
Sedgewick algoritmusa csakúgy, mint a fenti, egy félegyenest húz a vizsgálandó pontból egy fiktív, a
poligonon garantáltan kívülre eső ponton át, ám bármilyen legyen is ez (mindegy, hogy a poligon
csúcsain megy át vagy a poligon valamely oldalát tartalmazza ), ennek a félegyenesnek és a
poligonnak számolja össze a metszéspontjait, úgy, hogy közben figyeli az előforduló speciális
eseteket, de sajnos a fenti ábrán láthatókhoz hasonló esetekkel nem tud mit kezdeni.

Kapcsolódó problémák:

P2.2.1. Írjunk hatékony algoritmust, amely eldönti, hogy egy pont egy konvex poligon belsejében
van-el
P2.2.2. Írjunk olyan pascal függvényt, amely eldönti, hogy két háromszögnek van-e metszéspont ja.

Kapcsolódó versenyfeladatok

Feladat           Instrukció
RUGB92-3          Itt csak azt kell vizsgálni, hogy a sík egy adott pontja egy adott háromszög
                  belsejébe esik-e, ezért a feladat jóval egyszerűbb módszerrel is megoldható
IOAG91-2




                                                                                                       33
2.3. Ponthalmaz konvex burka

Ebben a fejezetben egy olyan       algoritmust kell készítenünk, amely meghatározza egy adott
ponthalmaz konvex burkát. A konvex burok nem más, mint egy konvex poligon, melynek csúcsai az
adott ponthalmaz pontjai közül valók és a többi pont a poligon oldalaira vagy belsejébe esik.




      Fig. 2.3.1.: Ponthalmazok   konvex hurka.

A pontosság kedvéért egyértelművé kell még tennünk azt az esetek, amikor a ponthalmaz 3 vagy több
pontja egy egyenesbe esik a ponthalmaz határán. Ilyenkor algoritmusunk outputját csak a poligon
csúcsaira eső pontok 'alkotják. Azaz a 2.3.1 b) ábrán látható ponthalmaz konvex burkát az
ABCDEFGH-val megjelölt pontokkal azonosítjuk.
A konvex burok egyik tulajdonsága, hogy bármely a poligonon kivüli egyenest a poligon felé
mozgatva az a poligont egy csúcsában érinti. Ezt a tulajdonságot alkalmazva a koordináta
tengelyekkel párhuzamos egyenesekre kapjuk, hogy a pontok közül azok, amelyek minimális ill.
maximális x illetve y koordinátákkal rendelkeznek biztosan a burokhoz fognak tartozni. Ezt a tényt
használjuk fel a soron következő algoritmusunk kiindulásaként.
Algoritmusunk bemenete pontok egy halmaza, amelyet egy array[J..max]          alpont fog reprezentálni.
A kimenete pedig egy poligon. Minthogy a poligon típus is egy előbbihez hasonló tömb típus, ezért
az algoritmus a bemenetként kapott tömb elemeit egyszeruen átrendezi, úgy, hogy az első M
tömbelem fogja a ponthalmaz konvex burkát reprezentálni.
Látnunk kell azt, hogy azzal, hogy megtaláltuk és valamilyen módon megjelöltük a ponthalmaz
konvex burkát alkotó pontokat, még nem oldottuk meg teljesen a problémát. Az így megjelölt
pontokat ugyanis úgy kell sorrendbe rendeznünk, hogy azok valóban poligont alkossanak. Azaz a
 2.3.1 b) ábrán látható ponthalmazt inputként megadva a GBCEDFAH output nem elfogadható.
Tehát a csúcspontként megjelölt pontokat, még rendeznünk kell. Ha ezen pontok (x, y) derékszögű
koordinátáit (r, d) polárkoordinátákká alakítjuk (ahol d az Origótól mért távolság, r pedig a pont
 helyvektorának valamely rögzített egyenessei vett szöge), majd a pontokat a hozzájuk tartozó r érték
 alapján rendezzük akkor a kívánt sorrendet kapjuk. Megfontolandó az, hogy az egész probléma nem
 vezethető-e vissza a ponthalmaz pontjainak (r, d) szerinti lexikografikus rendezésére!
 Algoritmusunk azonban explicit módon semmilyen rendezést nem fog tartalmazni, lesz azonban egy
 kódrésze, amely a fent leírtak szerinti legkisebb r értékü pontot választja ki, anélkül, hogy
 trigonometrikus    fuggvényeket használna. A trigonometrikus           fuggvények kiküszöbölése      az
 algoritmusból a megvalósított programot gyorsabbá, hatékonyabbá teszi. Az algoritmus:

       1 Válasszuk ki a minimális y koordinátájú pontok közül azt amelynek a legkisebb x
        koordinátája van.. A fentiek alapján ez a pont biztosan a konvex burok csúcspontja lesz.
        Legyen ez a burok első pontja.

       2 A burok második pontját úgy kaphat juk, hogy a burok első pontján át húzunk egy x-
         tengely jel párhuzamos egyenest (az előző pont garantálja, hogy ezen egyenes alatt, már
         nincsenek pontok), majd ezt az egyenest forgassuk az előző pont körül az óramutató


                                                                                                      34
járásával ellentétes irányban addig, amíg valamely pontba ütközünk. Ha egyszerre több
        pontba ütköztünk akkor válasszuk ki a távolabbi pontot, és ez lesz a burok következő pontja.
      3. A burok következő pontját úgy kapjuk, hogy egyenesünket az utoljára burokpontként
         megjelölt pont körül forgatjuk az eddigiekkel megegyező irányban addig amíg valamely
         pontba nem ütközünk. Ha egyszerre több pontot is elért az egyenesünk, akkor mindig a
         távolabbi pontot választ juk a burok következő pontjaként.

      4. Ismételjük a 3.-beli eljárást addig míg a kiindulási pontunkat el nem értük.

      Fig. 2.3.2.: Ponthalmaz konvex burkát előállító algoritmus.

 Az algoritmusbeli feltételek garantálják, hogy a burokbeli pontokat a helyes sorrendben állítjuk elő.
Az algoritmust megvalósító pascal program technikai megoldása az, hogy az így megjelölt pontokat
az input tömb elejére cseréli és a már beválasztott pontokat nem vizsgálja újra. A következő ábra az
algoritmus működését szemlélteti:




                  1                                            1


                                                                             4


                                            3                                           3




               1                                               1


                              4                                          4


                                                           5
              5                              3                                          3



                1                                              1


       Fig. 2.3.3.: A "csomagoló" eljárás működése.

 Visszatérve a konvex burok értelmezésére, megadására, az algoritmust és így a programot is
 egyszeruen át lehet alakítani, úgy, hogy az a burok csúcsoktói különböző pontjait is előállítsa


                                                                                                    35
outputként. Egyszerűen akkor, amikor egyenesünk több ponttal ütközik egyszerre, akkor az érintett
pontok közül a legközelebbit kell a burok következő csúcspontjaként megjelölnünk.
Mindezek után az algoritmust megvalósító pascal program:

program     becsomagoli
const
          max=100i
type
         pont=record
                x:integeri
                y:integer
                erid r
var
         inp,out:texti
         ok,s:stringi
         n,i,j,k:integeri
         poligon = array[O ..max]          of ponti

procedure   str_to_pont(s:stringivar                q:pont)i
 var v,j,xl,x2:integeri
      sl:stringi
begin
 v:=pos(',',s)i    sl:=copy(s,l,v-l)i
 delete(s,l,v)i    val(sl,xl,j)i
 val(s,x2,j)j
 q.x:=xli q.y:=x2i
e nd r


function theta(pl,p2:pont)  :reali
var
 dx,dy,ax,ay:integeri
 t:reali
be gin
 dx:=p2.x-pl.xiax:=abs(dx)i
 dy:=p2.y-pl·Yiay:=abs(dY)i
 if (dx=O) and (dy=O) then t:=-l else t:=dy/(ax+aY)i
 if dx<O then t:=2-t else if dy<O then t:=4+t;
 theta:=t*90.00
endi

function d(pl,p2:pont)            :reali
var
 dx,dy:reali
begin
 dx:=p2.x-pl.Xi
 dy:=p2.y-pl·Yi
 d:=sqrt(dx*dx+dy*dy)
endi

function csomag (c:integer) :integeri
var
 i,min,m:integeri
 minszog,v,mintav,th:reali
 t:ponti
begin
 min:=li

                                                                                                36
for i:=2 to n do
  if poligon[i] .y<poligon[min].y       then min:=i
  else
    if poligon[i] .y=poligon [min] .y
    then
     if poligon[i] .x<poligon[min].x      then min:=i;
 m:=O;
 poligon[n+l] :=poligon[min];
 minszog:=-l;
 repeat
  inc (m) ;
  t:=poligon[m];poligon[m]     :=poligon[min];poligon[min]   :=t;
  min:=n+l;
  v:=minszog;
  minszog:=360.00imintav:=O;
  for i:=m+l to n+l do
    begin
     th:=theta(poligon[m],poligon[i])i
     if th >= v then
       if th < minszog then
        begin
         min:=ii
         minszog:=th;mintav:=d(poligon[m],poligon[min])
        end
       else
        if th     minszog
        then
         if c*d(poligon[m],poligon[i])       > c*mintav
         then
            begin
             min:=i; minszog:=thi
             mintav:=d(poligon[m),poligon[min))
            end
     end
  until min=n+l;
   csomag:=m
 end;
begin
 assign(inp, 'pontok.inp');
 assign(out, 'burok.out');
 reset(inp);     rewrite(out);
 repeat
 readln(inp,s);
   n:=O;
   while s<>'*' do
    begin
      inc (n);
      str_to_pont(s,poligon[n))i
      readln(inp,s)
    end;
    for i:=l to csomag(-l) do
     writeln(out,poligon[i)     .x,',' ,poligon[i) .y) i
    writeln (out, '*')
 until eof(inp)i
close(inp);close(out);
end.

                                                                    37
Fig. 2.3.4.: A Konvex Burok algoritmus.

A program a 'pontok imp' nevű imput állományból olvassa be a ponthalmazokat, és a 'burok.out'
nevű output állományba írja a megfelelő konvex burok csúcspontjait. Az állományok szerkezete a
következő:

Az input állomány blokkokból áll, egy blokk egy ponthalmaz pontjainak megadását tartalmazza,
úgy, hogy egy sorban pontosan egy pont kordinátái találhatók vesszővel elválasztva. A blokk végét '*'
jelzi.
Az output állomány az inputként megadott ponthalmazok konvex burkát tartalmazza . Egy sorban
pontosan egy pont koordinátái vesszővel elválasztva. A burok végét egy új sorban egy '*' jelzi.

A program csomag(c:integer)       eljárása végzi a fent jellemzett eljárást. A c:integer paraméter
értékétől függ, hogy az output állományba, melyik megadás szerint kerüljenek a poligon pontjai. c >
O esetén, csak a poligon csúcspontjai, c < O esetén azon pontok is, amelyek a poligon oldalaira
esnek

A probléma általánosításának egyik lehetséges módja a több dimenziós térre történő kiterjesztése.
Könnyű belátni, hogy a fenti algoritmus több dimenziós térben is helyesen működik, ha elkészítjük a
fenti pascal program több dimenziós, hipersíkokat is kezelő változatát.

Kapcsolódó Problémák:

P2.3.l. Írjunk olyan algoritmust, amely online módon adja meg az inputként megadott ponthalmaz
konvex burkát. Az input pontokat egymás után egyesével adjuk meg az eljárásunknak és az eddig
megadott pontok konvex burkát adja meg lépésenként, módosítva azt minden újabb pont után ha
szükséges. lásd [SHAM1977]

Kapcsolódó versenyfeladatok:

Feladat           Instrukció
IOAG91-2          A feladat megoldása mindkét eddig ismertetett            algoritmus    alkalmazását
                  megköveteli
ACMF92-4          A konvex burok algoritmus explicit alkalmazása.
KLTE93-2


Irodalomjegyzék      a 2.3 fejezethez


 [SHAM1977]:     Shamos, M. 1. 1977, Computational geometry
 [GRAHI972]:     Graham, R. L. An efficent algorithm for determining the convex hull ofa fmite
                 planar set. Inform. Processing Letters 1 1972. P 132-133
 [PREP 1977]:    Prepatra, F. P., Hong S. J.: Convex hulls of fmite sets in two and three dimensions.
                 Communications of ACM, Vol 20. Num 2., p 87-93




                                                                                                        38
3. Gráfalgoritmusok

Bevezetés

A programozói versenyek "nehezebb" feladatainak legnagyobb hányadát a gráfelméleti feladatok
teszik ki. Ezek a feladatok lehetnek expliciten gráf elméleti fogalmakkal megfogalmazottak
(olyanokkal, mint feszítőfa vagy legrövidebb út), vagy valamilyen a mindennapi életből vett köntösbe
bújtatottak.
A gráf, mint matematikai objektum nem más, mint csúcsok és élek véges halmaza. A csúcsok a
legkülönfélébb más objektumok lehetnek, egyetlen kikötésünk, hogy valamilyen megkülönböztetésük
létezzen: legyen nevük vagy más azonosítójuk. A gráf egy éle összeköttetést, kapcsolatot reprezentál
 a gráf pontosan két csúcsa között.



                                  ®


A fenti ábrán látható ABCDF, G, EHI csúcspontokkal jellemzett rajzok alkothatnak egy kettő vagy
három gráfot. Tekinthetjük például ABCDFG-t egy gráfnak. Ezt a gráfot definiálhat juk úgy, hogy
felsoroljuk a csúcsait: A, B, C, D, F, G majd az éleit AF, AD, BC, BD, Bf, CD.
Egy X csúcspontból pontosan akkor vezet út egy Y csúcsba egy gráfban, ha létezik a csúcsoknak egy
olyan listája, amelyben az egymás után következő csúcsok a gráfban éllel vannak összekötve. A gráf
összefüggő minden csúcsából létezik út minden más csúcsába. Pl a fenti ABCDF gráf összefüggő,
míg az ABCDFEHI gráf nem összefüggő. A gráf egy egyszerű útja azon út amelyhez tartozó
listában nincs ismétlődő csúcspont. A gráf egy kör-e azon egyszerű út, amelyben a kezdő és a
végpont megegyezik (vagyis a listában egyetlen ismétlődés van). Azt a gráfot, amelyben egyetlen kör
sincs és összefüggő fá-nak nevezzük (a nem összefüggő, körmentes gráf ot erdő-nek nevezzük). A
gráf feszítőfá-ja a gráf azon részgráfja, amely a gráf összes csúcsát tartalmazza, és pontosan annyi
élt a gráf élei közül, hogy az így nyert részgráf összefüggő legyen. Könnyen látható, hogy n csúcsú
gráf feszítőfájának pontosan n-l éle van. A feszítőfa másik ismert tulajdonsága, hogy bármely új élt
hozzáadva már kört kapunk. Azt a gráf ot, amely rendelkezik az összes lehetséges éllel teljes gráf-
nak, amelyik viszonylag kevés éllel rendelkezik ritka gráf-nak, amelyik elég sok éllel rendelkezik
 sűrű gráf-nak nevezzük.
 A gráf ok gépi reprezentációiról nagyon hasznos dolgokat tudhatunk meg [SEDGI988]-ban valamint
 [NIE 11977] -ben.
 A gráf algoritmusok bonyolultsága (itt most bonyolultságon az adott körülmények közötti legrövidebb
 idejű megvalósíthatóságot kell érteni, ennek egyik leglényegesebb komponense a kód rövidsége és
 egyszerűsége) nagyban függ attól, hogy a gráfok milyen reprezentációját választottuk. Általában a
 legegyszerűbb kódot azon algoritmusok eredményezik, amelyekben a gráf reprezentációjaként
 valamilyen mátrixos (szomszédsági vagy összefüggőségi) megoldást választottunk. Persze vannak
 problémák, amelyeknél a listás reprezentáció elkerülése sokkal "költségesebb" lenne, mint annak
 megvalósítása.


 Irodalomjegyzék       a 3. fejezethez

 [SEDGI988]:       Robert Sedgewick: Algorithms pp418 - 421
 [NIEI1977]:       J. Nievergelt, J. C. Farrar, E. M. Reingold: Matematikai Problémák Megoldásainak
                   Számítógépes Módszerei. 72-73 oldal



                                                                                                      39
3.1. Mélységi keresés

Az egyik legklasszikusabb gráf elméleti algoritmus a keresés. Ebben a problémában olyan kérdésekre
kell az algoritmusnak válaszolnia, mint van-e az adott gráfban kör, vagy melyek a gráf összefüggő
komponensei.
Fogalmazzuk meg először, mit is kell most keresésen értenünk! Legyen adott a gráfnak két csúcsa,
nevezetesen START és GOAL, a feladat megkeresni a STARTból a GOALba vezető utat, vagyis azt
a listát amelynek első elem START utolsó eleme pedig GOAL és az egymás után következő
listaelemeket a gráfban él köti össze.
        l. Meg kell vizsgálnunk, hogya START csúcs megegyezik-e a GOAL-al, ha igen, akkor
          kereső eljárásunk már véget is ért hiszen a keresett listának egyetlen elemből áll: a ST ART
          csúcsból.
        2. Ha nem vagyunk ilyen "szerencsések", akkor keressük meg a START csúcs összes
          szomszédját, ezt a folyamatot kiterjesztésnek nevezzük. Az így nyert csúcsokat a START
          csúcs "gyermekeinek", "utódjainak" vagy "rákövetkezőinek" nevezzük. Ennek a lépésnek az
          eredménye egy lista.
        3. Eztán a fenti két lépést alkalmazzuk az így nyert új csúcsokra, úgy, hogya 2. lépés után az
          újabb listát az előző listához fűzzük.




                                p.z S1 csúcs kiterjesztése




       Fig. 3.1.1.: Altalános kereső eljárás működése

A módszerek különbözőségének okát abban kell keresnünk, hogy a 2. lépés után kapott csúcsokat
milyen sorrendben terjesztjük ki. A két legismertebb kereső stratégia a szélességi és a mélységi
keresés. A mélységi keresés alkalmával mindig olyan "messze" haladunk a gráfban a START
csúcstól, amilyen messze csak lehet, vagyis a fenti ábrán mélységi stratégia szerint az S II csúcs
következne kiterjesztésre, szélességi stratégia szerint pedig az S2. De mindennél többet mond talán az
algoritmus formális leírása.

       INPUT: A gráf két csúcsa START és GOAL
       FELADAT: Meghatározni, hogy létezik-e egyszerű út START és GOAL között.
       OUTPUT: Igen vagy Nem
       1. Legyen NYILTAK egy lista, első eleme legyen START
         Legyen ZÁRT szintén egy üres lista
       2. Ha NYILTAK üres, akkor algoritmus vége és a válasz Nem
       3. Vedd az első csúcsot a NYILTAK-ból legyen ez A. Szúrd A-t a ZÁRT lista elejére.
         a) Ha A = GOAL akkor az algoritmus vége és a válasz Igen.
         b) Hajtsd végre a kiterjesztést A-ra, ennek eredménye legyen az UTÓDOK listája.
                  b1. Vedd ki az UTÓDOK listájából azon csúcsokat, melyek szerepelnek a
                  ZÁRT listában
                  b2. Fűzd az UTÓDOK listáját a NYILTAK listája elé.
                  b3 Menj 2. -re

       Fig. 3.1. 2.: A mélységi keresés algoritmusa.

                                                                                                    40
A fenti algoritmus, nem használja ki az élek reflexivitását, vagyis irányított gráf okra is tökéletesen
működik. A mélységi keresési stratégiát természetesen nem csak arra használhatjuk, hogy
megkeressük két csúcs között egy gráfbeli utat. Használhatjuk pl. arra is, hogy megtudjuk, hogy egy
adott gráf tartalmaz-e kört. Ugyanis ha a 3bl-ben ténylegesen ki kell vennünk valamely csúcsot a
ZÁRT listából, akkor ez azt jelenti, hogy ehhez a csúcshoz egy újabb útvonalon eljutottunk, vagyis,
hogy a gráf kört tartalmaz. Azt is könnyen beláthatjuk, hogy a stratégia segítségével leválogathatjuk
egy gráf összefiiggő komponenseit: Meg kell jelölnünk a kiterjesztett csúcsokat és ha a NYILTAK
listája már üres és még van a gráfban meg nem jelölt csúcs, akkor ez azt jelenti, hogy ez a meg nem
jelölt csúcs a gráf eddigitől különböző komponensében van.
 A témával kapcsolatban nincs már más hátra, mint a fenti ellenőrzéseket elvégző pascal program:

program deptfirsti
{
    input formatum (a graf megadasa)
    1.    sor   csucsok szama (v)
    2.    sor   elek szama     (e)
    2+1.  sor   elso ,1: a ket csucs sorszama amelyeket osszekot
                          [space]-szel elvalasztva.
    2+2.  sor   masodik,l


    2+e      sor      e-edik ,1


const maxV=200;

type link=Anode;
     node=record
       v     :integer;
       next :link
          end;

var inp,out:text;
    id,k,l,v,e,last:integer;

       components:integer;             {ennyi osszefuggo komponens}

       graph: array[l ..MaxV] of link;
       order: array[l ..MaxV] of integer;
       cycle:boolean       {=true ha van benne kor                               };

procedure Str2Vertex(s:string;var k,j:integer);
var v,h:integer;
      xl,x2:integer;
      sl:string;
 begin
  v:=pos(' ',s); sl:=copy(s,l,v-l);
  delete(s,l,v); val(sl,k,h); val(s,j,h)
 end;

procedure ReadInput;
var
 j,i,x,y:integer;
 s:string; t:link;
begin
 assign(inp, 'deptlst.inp');                  reset(inp); readln(inp,v);

                                                                                                     41
readln(inp,e);   for i:=l to v do graph[i] :=NIL;
 for j:=l to e do
  begin
   readln(inp,s);Str2Vertex(s,x,y);
   new(t);tA.v:=x;   tA.next:=graph[y];graph[y]  :=t;
   new(t);tA.v:=y;   tA.next:=graph[x];graph[x]  :=t;
  end;
end;

Procedure visit(k:integer);
 var t:link;
 begin
  inc(id);order[k]   :=id; t:=graph[k];
  while t<>NIL do
   begin
     if order[tA.v]=O   then visit(tA.v)                 else    cycle:=true;
     t:=tA.next
    end
 end;

 Procedure Conclusion;
 begin
  writeln(out, 'Osszefuggo komponensek:     ',components);  {/ A /}
  if cycle then writeln('kor   :van ')
  else if components=l   then writeln('fa')   else writeln('erdo');
  writeln(out)
 end;

begin
 assign(out, 'deptlst.out');      rewrite(out);
 ReadInput;
 id:=O; last:=O; components:=O;
 for k:=l to v do order[k] :=0;
 for k:=l to v do
 begin last:=idi
   if order[k]=O
   then begin
     visit(k);   inc(components);
     writeln(components,        osszefuggo reszgraf:');
     for 1:=1 to v do
       if (order[l]<=id)   and (order[l]>last)   then write(l,                       I   ')i
     writeln;
   end
 end;
 Conclusion;
  close(out)
end.


      Fig. 3.1.3.: A mélységi keresés.

A program feltételezi, hogy az input fileban megadott csúcsok és élek egyetlen gráfhoz tartoznak. A
program az előzőek szerinti stratégiát használva alkalmas:

      -a gráf bejárására (minden csúcsot pontosan egyszer meglátogat)
      - a gráf összefüggő komponenseinek leválogatására

                                                                                                42
- eldönti egy gráfról, hogy van-e benne kör; (nem sorolja fel a köröket)
     - ezek alapján eldönti, hogy a gráf fa ill erdő-e.

A gráf reprezentációja most egy tömb. A tömb elemei listák, minden csúcshoz pontosan egy. a lista
elemei az adott csúcs szomszédjai. Vagyis a fenti gráfot programunkban a következő tömb
reprezentálja:

               graph[1]=      6 -> 4
               graph[2]=      3 ->   4 -> 6
               graph[3]=      2 ->   4
               graph[4]=      2 ->   3
               graph[5]=      8 ->   9
               graph[6]=      1 ->   2
               graph[7]=
               graph[8]=      5 -> 9
               graph[9]=      5 -> 8

Könnyű látni, hogy ez a reprezentáció alkalmas irányított gráf ok gépi reprezentálására is. Irányított
gráfok esetén egy adott élt, csak egyszer kell szerepeltetni a struktúrában.
Az order[l..MaxVJ tömb tartalmazza a gráf bejárási sorrendjét, kezdetben minden eleme O. A gráf
azon összefuggő komponensének mélységi bejárását, amely a paraméterként megadott csúcsot
tartalmazza a rekurzívan működő visít eljárás végzi. A visít eljárást (a kiterjesztést) a l-es csúcsra
végrehajtva az eljárás megkeresi az l-es csúcs szomszédjai közül, azt amely még nem volt megjelölve
az order tömb segítségével. Ha talál, ilyet akkor erre végrehajtja a visít eljárást, ha nem talál ilyet
akkor ez azt jelenti, hogy, az adott csúcs minden szomszédját bejártuk, vagyis vissza kell lépnünk egy
szinttel "feljebb" (backtrack) ....
Egy gráf összefüggő komponenseinek leválogatására alkalmas Warshall algoritmusa is [NIE21977]


Kapcsolódó versenyfeladatok

 Feladat          Instrukció
 KLTE91-5         Megoldható más, nem gráf elméleti módszerrel is. A gráf os módszer megvalósítása
                  hosszabb és bonyolultabb módszert követel.
 IOAG91-4         A feladatban egy gráf legtöbb csúcsot tartalmazó összefuggő komponensét kell
                  megkeresnünk, ez a fenti program segítségével lehetséges. Mivel a feladatban a
                  gráf a szomszédsági mátrixával adott, ezért Warshall algoritmusa is nagyon jól
                  alkalmazható.
 ACMF91-1         Útkeresés.
 KLTE92-3         Körkeresés
 KLTE92-5         Útkeresés.
 USEC92-3         Útkeresés.
 TUBP92-5         Összefuggő komponensek szétválasztása.
 ACMF92-5         Vegyünk sorra minden élt a feladat irányított gráfjában. Majd az adott élt hagyjuk
                  el az eredeti gráfból . Ha ebben a gráfban létezik út az adott él végpontjai között
                  akkor az adott él által reprezentált függőség redundáns.


Irodalomjegyzék      a 3.1 fejezethez

 [NIE21977]:     J. Nievergelt, J. C. Farrar, E. M. Reingold: Matematikai Problémák Megoldásainak
                 Számítógépes Módszerei. 77 - 78 oldal.




                                                                                                     43
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms
Algorithms

Mais conteúdo relacionado

Mais de Csaba Kiss

Nia 2011 q1-r00.99 - tier2 datasheet - magyar
Nia 2011 q1-r00.99 - tier2 datasheet - magyarNia 2011 q1-r00.99 - tier2 datasheet - magyar
Nia 2011 q1-r00.99 - tier2 datasheet - magyarCsaba Kiss
 
Microsegment corpus 01.32 statistics
Microsegment corpus 01.32   statisticsMicrosegment corpus 01.32   statistics
Microsegment corpus 01.32 statisticsCsaba Kiss
 
Nia 2010 q4-r00.98 - tier2 datasheet - magyar
Nia 2010 q4-r00.98 - tier2 datasheet - magyarNia 2010 q4-r00.98 - tier2 datasheet - magyar
Nia 2010 q4-r00.98 - tier2 datasheet - magyarCsaba Kiss
 
Microsegment Corpus 01.30
Microsegment Corpus 01.30Microsegment Corpus 01.30
Microsegment Corpus 01.30Csaba Kiss
 
Nemzeti Infrastruktúra Adatbázis
Nemzeti Infrastruktúra AdatbázisNemzeti Infrastruktúra Adatbázis
Nemzeti Infrastruktúra AdatbázisCsaba Kiss
 
National Infrastructure Database
National Infrastructure DatabaseNational Infrastructure Database
National Infrastructure DatabaseCsaba Kiss
 
Long Tail Business Model and OSS/BSS
Long Tail Business Model and OSS/BSSLong Tail Business Model and OSS/BSS
Long Tail Business Model and OSS/BSSCsaba Kiss
 
Meet Linda And Mark Microsegment E20
Meet Linda And Mark Microsegment E20Meet Linda And Mark Microsegment E20
Meet Linda And Mark Microsegment E20Csaba Kiss
 
"Kapcsolat" Concert - "Relationship" Concert
"Kapcsolat"  Concert - "Relationship" Concert"Kapcsolat"  Concert - "Relationship" Concert
"Kapcsolat" Concert - "Relationship" ConcertCsaba Kiss
 

Mais de Csaba Kiss (9)

Nia 2011 q1-r00.99 - tier2 datasheet - magyar
Nia 2011 q1-r00.99 - tier2 datasheet - magyarNia 2011 q1-r00.99 - tier2 datasheet - magyar
Nia 2011 q1-r00.99 - tier2 datasheet - magyar
 
Microsegment corpus 01.32 statistics
Microsegment corpus 01.32   statisticsMicrosegment corpus 01.32   statistics
Microsegment corpus 01.32 statistics
 
Nia 2010 q4-r00.98 - tier2 datasheet - magyar
Nia 2010 q4-r00.98 - tier2 datasheet - magyarNia 2010 q4-r00.98 - tier2 datasheet - magyar
Nia 2010 q4-r00.98 - tier2 datasheet - magyar
 
Microsegment Corpus 01.30
Microsegment Corpus 01.30Microsegment Corpus 01.30
Microsegment Corpus 01.30
 
Nemzeti Infrastruktúra Adatbázis
Nemzeti Infrastruktúra AdatbázisNemzeti Infrastruktúra Adatbázis
Nemzeti Infrastruktúra Adatbázis
 
National Infrastructure Database
National Infrastructure DatabaseNational Infrastructure Database
National Infrastructure Database
 
Long Tail Business Model and OSS/BSS
Long Tail Business Model and OSS/BSSLong Tail Business Model and OSS/BSS
Long Tail Business Model and OSS/BSS
 
Meet Linda And Mark Microsegment E20
Meet Linda And Mark Microsegment E20Meet Linda And Mark Microsegment E20
Meet Linda And Mark Microsegment E20
 
"Kapcsolat" Concert - "Relationship" Concert
"Kapcsolat"  Concert - "Relationship" Concert"Kapcsolat"  Concert - "Relationship" Concert
"Kapcsolat" Concert - "Relationship" Concert
 

Algorithms

  • 1. Programozói versenyfeladatok, alapvető matematikai algoritmusok Kiss Csaba Zsolt Programtervező matematikus KLTE Debrecen 1994.
  • 2. Tartalomjegyzék Bevezetés 2 A versenyekről. 3 1. Kombinatorikai algoritmusok 6 1.1. Ismétlés nélküli permutációk. 6 1.2. Ismétléses permutációk 10 1.3. Kombinációk 13 1.4. Egy halmaz összes részhalmazai, particionálása 16 1.5. Gray kódok 23 2. Geometriai algoritmusok 26 Bevezetés 26 2.1. Szakaszok metszete 28 2.2. Poligon és pont 31 2.3. Ponthalmaz konvex burka 34 3. Gráfalgoritmusok 39 Bevezetés 39 3.1. Mélységi keresés 40 3.2. Optimális keresés 44 Függelék 52 Ábrák, programok listája 52 Problémák listája 52 1
  • 3. Bevezetés A dolgozat címében hivatkozott versenyfeladatok az ACM szervezésében rendezett egyetemi programozói csapatversenyek feladatai. A versenysorozatot évente rendezik és három fordulóból áll. Az első forduló általában valamilyen helyi (egyetemi, városi) verseny. Ezeket a versenyeket a helyi (lelkes) szervezők szervezik, általában önkéntes alapon. Szervezésen itt a verseny tényleges lebonyolítását kell érteni és a feladatok kiválasztását. A helyi versenyekről tovább jutó csapatok vesznek részt a második fordulóban (egyetemenként legfeljebb egy csapat), amelyet már az ACM adott szervezete szervez. A második fordulóban (Regional Final - területi döntő) általában valamilyen nagyobb területi egység (Pl. Kalifornia, Nyugat-Európa) egyetemeinek csapatai versenyeznek. A területi döntőkből egy-három csapat juthat tovább a döntőbe, amely mindig valamelyik USA-beli nagyvárosban zajlik. A dolgozatom célja a verseny és a verseny feladatainak általános bemutatása. Egyetemi éveim alatt összesen 7 -szer vettem részt 1. és 2. fordulós versenyeken. Az itt összegyüjtött tapasztalatokat és szép megoldásokat, a megoldásokhoz felhasmált alapvető matematikai algoritmusokat úgy éreztem mindenképpen szükséges rendszerezve megörökíteni, ehhez kínált kiváló fórumot a diploma dolgozatom. A dolgozat melléklete kb. 90 eredeti versenyfeladatot tartalmaz, amely biztosan nagyon hasznos lesz az egyetemünkön folyó első és másod éves programozó-matematikus, matematikus, informatika és matematika tanár szakos hallgatók különböző tantárgyainak gyakorlati oktatásához. . A feladatok angol nyelvűek, mert a versenyek hivatalos nyelve az angol. A dolgozatban található alapvető matematikai algoritmusok segítségével mintegy 30 (általában a nehezebbek közé sorolt probléma) oldható meg. Az algoritmusok kisebb módosításaival további feladatok megoldhatók. Az egyes algoritmusoknál hivatkozás található a feladatokra, amelyek megoldásához az adott algoritmus jó hatásfokkal felhasználható. Az egyes algoritmusok újabb problémákat vetnek fel (P-jelű problémák) ezek megoldása szintén jó gyakorlat lehet. Ezúton szeretnék a csapatom nevében köszönetet mondani szakmai támogatóínknak és tanárainknak akik nélkül még az így elért szerény eredményeinket sem értük volna el, név szerint: Kuki Attilának a helyi versenyek szervezéséért és az európai döntőre való kiutazás szervezéséért, valamint hasznos tanácsaiért, Dr Arató Mátyásnak, Dr Lajkó Károlynak, Dr. Juhász Istvánnak és Dr Végső Jánosnak általános támogatásaikért, Herendi Tamásnak igen hasznos szakmai tanácsaiért és az IFSz Kft. valamint az IQSoft Rt. munkatársainak szakmai és egyéb támogatásukért. Végül szeretnénk megköszönni anyagi támogatásukat azon cégeknek és szervezeteknek akik nélkül semmiképpen sem képviselhettük volna egyetemünket az európai döntőkön: A Külkereskedelmi és Hitel Bank Rt., A Biogal Rt., Az IQSoft Rt., A Dataware Kft., A KL TE Diákönkormányzata. Valamint szeretném megköszönni csapattársaimnak Fekete Zoltánnak, Jakó Jánosnak, Molnár Tamásnak és tanáraimnak Kuki Attilának és Herendi Tamásnak a dolgozat elkészüléséhez adott hasznos tanácsaikat és bölcs észrevételeiket. 2
  • 4. A versenyekről A verseny szabályai: A versenyeken kezdetben 4, majd (1991 után) 3 fós csapatok indulhattak. A csapat tagjai között kezdetben lehetett egy diplomás is, később diplomások részvétele nem volt megengedett. A verseny ideje általában 5 óra. A csapatok a verseny ideje alatt egy darab IBM PC típusú számítógépet használhatnak a szükséges fordítóprogrammal felszerelve, ez a TURBO PASCAL 5.0 -ás verziója volt. A csapatok a verseny alatt bármilyen írott forrást használhatnak, viszont semmilyen más (pl. mágneses) forrás használata nem megengedett csakúgy, mint a programozható zsebszámológépek használata sem. A csapatoknak a verseny ideje alatt 5-8 problémát kell megoldaniuk. A problémák között semmilyen sorrendiség sincs. A verseny nyelve angol, ezért a feladatok szövege, a zsűrinek feltett kérdések, a zsűri válaszai is angol nyelvűek. A problémák megoldása A csapatoknak az elkészült megoldást -amely mindig egy pascal program forráskódja - az e célra fenntartott mágneslemezen kell a zsűrihez eljuttatniuk. A zsűri a kódot lefordít ja és saját input adataival teszteli. A programoknak 1 perc futásidő áll rendelkezésére, ez alatt kell outputot produkálniuk. Az output alapján a zsűri a következő válaszokat adhatja a csapatoknak: 1. Syntax Error - fordítási hiba 2. Run Time Error - Futás közbeni programhiba, pl O-val való osztás 3. Time Limit Exceded - Időtúllépés 4. Wrong Answer - hibás válasz. 5. Accepted - Elfogadva Egy probléma megoldásával többször is lehet próbálkozni, de zsűri csapatonként és problémánként méri a verseny kezdetétől a megoldáshoz felhasznált időt. Az 1.-4. esetben (és minden további sikertelen kísérlet után) a zsűri az adott csapatnak adott probléma megoldásához felhasznált idejét 20 perccel növeli. A verseny ideje alatt az egyes feladatokkal kapcsolatban felmerült értelmezési stb. problémákkal kapcsolatban a csapatok írásban kérdéseket tehetnek fel a zsűrinek, aki szintén írásban köteles válaszolni ezekre. A zsűri a csapatokat szabályszegés esetén kizárással sújthatja. A Kiértékelés szabályai: A verseny végeztével a zsűri összeszámolja az egyes csapatok által megoldott problémák számát, és összeadja a helyesen megoldott problémákhoz felhasznált időket. Így minden csapat eredménye két mennyiségből áll: - A megoldott problémák száma - Az ehhez felhasznált idő Az a csapat a verseny győztese, amely a legtöbb feladatot oldotta meg, ha ilyen több van akkor a verseny győztese az a csapat, amely a legkevesebb időt használta fel. Hazai Versenyek Az egyetemi programozói versenyek története Magyarországon 1990-ben kezdődött egy a Budapesti Műszaki Egyetemen (BME) rendezett versennyel, ahol nemcsak a BME csapatai, hanem a fóvárosi egyetemeken kívül vidéki csapatok is indultak. Ezen a versenyen a KL TE 3 csapatot indított, melyek a középmezőnyben végeztek. 1991 után minden évben a fóvárosban és Debrecenben is rendeztek versenyeket. Ezeken a versenyeken az induló csapatok száma nagyjából állandó volt: Budapesten kb 25, míg Debrecenben kb 10. 3
  • 5. A versenyek tapasztalatai A következő néhány mondatban a versenyeken, a problémák megoldásával kapcsolatban szerzett tapasztalatokról szeretnék írni. A verseny kezdetén érdemes minden feladatot átolvasni és értelmezni. Az egyes csapatokon belül többféle megoldási módszer is kialakulhatott, az egyik lehetséges, hogy az értelmezés után a csapattagok egymás között szétosztják a feladatokat és eztán egyenként, vagy problémás feladat esetén együtt keresik a megoldást és valósítják meg a kivitelezést. A másik módszer, hogy minden feladat elvi megoldását a csapattagok együtt keresik, csak a konkrét megvalósítás ideje alatt dolgoznak különböző feladatokon a csapattagok. De ezektől különböző más módszerek is kialakulhattak, valószínűleg erre nincs általános alkalmazható stratégia. A feladatok megoldásakor szerenesés esetben, amikor egyszerre több feladat elvi megoldása is elkészült, a szűk keresztmetszetet a rendelkezésre álló egyetlen számítógép gépideje (5 óra) jelenti. Érdemes a legkönnyebb, legegyszerűbb probléma megoldásának megvalósításával kezdeni. A megoldásokban nem kell szépségre és az eleganci ára törekedni, mert a zsűri ezt nem értékeli. Sokkal inkább a kód egyszerűségére és átláthatósága kell, hogy a cél legyen. A feladatok elég nagy része többféleképpen is megoldható. Azoknál a feladatoknál, ahol a megoldást el lehet érni a feladatbeli objektumok összes esetének (pl. összes permutáció) vizsgálatával, ott a program rendelkezésére álló 1 perces futási időre kell figyelnünk, azaz tisztában kell lennünk az átlagos PC-k (különösen a zsűri által használt PC) .:» gyorsaságával. Viszont ha az összes eset vizsgálata belefér az egy perces futási időbe, nem érdemes a szép és "eszes" megoldás megkeresésévei foglalkozni. A későbbiekben az adott helyen az egyes algoritmusok futási idejére utalni fogunk. A mellékletben található feladatok A mellékletben található feladatokra a dolgozatban a feladatok azonosítójával hivatkozunk, amely XXXXéé-n alakú, ahol XXXX a verseny helyszínének rövidítése, éé a verseny megrendezésének évszáma évszázad nélkül, n pedig a feladat sorszáma. A mellékletben a feladatok évszám szerint növekvő sorrendben találhatók. A formátumuk eltér az eredetitől, hogy egységesen kezelhessük őket. A legnagyobb része a feladatoknak négy jól körbehatárolható csoportból kerül ki. Ezek a következőek: 1. Szimulációs feladatok Ezekben a feladatokban jól definiált objektumokkal találkozhatunk, amelyekhez szabályok tartoznak. Az objektumok ezen szabályok szerint viselkednek. A megoldáshoz nem kell egyéb, mint a feladat pontos megértése, az objektumok megfelelő gépi reprezentál ása és a szabályok pontos programozása. Könnyebb szimulációs problémák: USSC85-3, KLTE91-1, RUGB91-1, RUGB91-2, ACMF91-5, IOAG91-1, ODUN92-1, RUGB92-2, RUGB92-5, KLTE92-2, USEC92- 2, ACMF92-1, KLTE93-4 Nehezebb szimulációs problémák:KLTE91-2, RUGB91-5, RUGB91-7, ACMF91-3, ACMF91- 4, RUGB92 -6, TUBP92-1, A szimulációs feladatok megoldásával a dolgozatban nem foglalkozunk. Megoldásukat a Pascal nyelvvel ismerkedőknek ajánlhatjuk. Megoldásukhoz csak alapvető matematikai ismeretekre van szükség. 2. Kombinatorikai feladatok lásd a dolgozat első fejezetét 3. Geometriai feladatok lásd a dolgozat második fejezetét 4. Gráfelméleti problémák lásd a dolgozat harmadik fejezetét. 4
  • 6. Az egyes versenyek helyszínei és időpont ja: Rövidítés Színt Helyszín Időpont (forduló) USSC85 2 ?, California , USA 1985. KLTE91 1 KL TE, Debrecen 1991. Június IOAG91 * Athen, 3rd International 1991. Május Olympiad in Informatics TUBP91 1 BME, Budapest 1991. Október RUGB91 2 Gent, Belgium 1991. November ACMF91 3 USA 1991. USSC92 2 ?, USA 1992. ODUN92 1 Norfolk, Va USA 1992. Szeptember TUBP92 1 BME, Budapest 1992. Október KLTE92 1 KL TE Debrecen 1992. Október RUGB92 2 Gent, Belgium 1992. November ACMF92 3 Indíanapolis, USA 1992. KLTE93 1 KL TE, Debrecen 1993. Szeptember 5
  • 7. 1. Kombinatorikai algoritmusok 1.1. Ismétlés nélküli permutációk Vizsgáljuk először az ismétlés nélküli permutációk generálásának problémakörét. A probléma pontos defmíciója a következő: Adott a P={ 1,2 ..n} halmaz, előállítandó a) az összes permutációja tetszőleges sorrendben. b) az összes permutáció ja lexikografikus sorrendben. c) a lexikografikus rendezés szerinti i-edik permutációja A P összes permutációinak halmazát jelöljük P! -al : P! = {~, pz 'o 00' Pn! } Először az egyik legegyszerubb módszer bemutatásával kezdjük. A módszert Fike publikálta 1975- ben [Fike1975] , majd 1976-ban Rohl módosította [Roh1l976] . Legyen S={(d2,d3, ••• ,dn) II~dk s k: k=2,3, .. n} ekkor S összesen 2*3* ..*n=n! vektort tartalmaz. Vegyük észre, hogy S elemeit programmal könnyen lehet generálni: kis n esetén n-l darab egymásba ágyazott ciklussal, ahol a ciklusváltozók értékei rendre az [1..2], [1..3], ... ,[1..n] intervallumokat futják be, nagy n esetén rekurzívan. Ha egyszeruen programozható egy-egy értelmű megfeleltetést adnánk S és P! elemei között akkor, mivel S elemeit könnyen generálhatjuk egyszeru módszert kapnánk P! generálására. A Fike módszere a következő egy-egy értelmű megfeleltetést adja S és P! elemei között: Legyen (d2, d3, ••• , dn) egyelem S-ből, ekkor a hozzátartozó P permutációt úgy kapjuk, hogy kiindulva ~ = (1,2, ... , n) - ből, mint kezdeti permutációból cseréljük fel P; -ben a k-adik elemet a dk -adikkal. Mindezek alapján az algoritmus először az S-beli elemeket generálja, majd ebből állítja elő a fent leírtaknak megfelelően a kapcsolódó permutációt. Észrevehető, hogy a fenti algoritmus redundáns elemeket tartalmaz (pl ha dk = k, akkor felesleges csere), ezen elemek kiküszöbölésére tett módosítást 1976-ban J. S. Rohl. A Rohl által módosított algoritmus pascal programja a következő: proeedure Fike_Rohl_perm(n:integer); var p:array[l ..max) of integer; { n <= max} i:integer; proeedure permute(k:integer); var temp, dk, dn:integer; begin if k=n then begin proe (p) ; temp: =p [nl ; for dn:=n-l downto 1 do begin p[n) :=p[dn) r p l dn ) :=temp; p.r t p j r oc p j dn l r=p I n l r end; p[n) :=temp; end else begin permute (k+l) ; temp :=p [k) ; for dk:=k-l downto 1 do begin p [k) :=p [dk) ; 6
  • 8. p[dk] :=tempi permute(k+l)i p [dk] :=p [k] i e rid r p[k] :=tempi endi endi be gin {Fike_Rohl perm} for i:=l to n do p[i] :=ii permute(2)i endi Fig. 1.1.1..' Fike algoritmusa Rohl módosításaival A versenyfeladatok megoldásánál a közölt eljárás általában jól használható, de azokban a problémákban ahol a feladat szempontjából n állandó és n nem túl nagy (n<6), elképzelhető olyan eljárás is amely n darab egymásba ágyazott ciklust tartalmaz az S elemeinek generálásához. Ennek az algoritmusnak a gyorsaság mellett a kód egyszerűsége is az előnye. A fenti módszer csupán az (a) problémára ad választ. Ha pl. valamely feladat a lexikografikus sorrendben követeli meg tőlünk a permutációk felsorolását, akkor a módszerek egy újabb családjával kell megismerkednünk [We1ll971] . Most egy olyan módszert mutatunk be, amely 1812-ből származik, első említése [FiscI812] majd [ShenI962] . A módszer lényege négy lépés alkalmazása = egy adott P (Pl' P2'''' Pn) permutációra, amely eredményeként a lexikografikusan következö permutációt kapjuk A négy lépés: (1) Legyen i a legnagyobb index, amelyre p i-l< Pi (2) Legyen} az a legnagyobb index, amelyre Pi-I<Pj (3) Cseréljük fel Pi-l -et p.-vel. (4) Fordítsuk meg Pl' Pi+I,··.,Pnsorrendjét. Fig. 1.1.2 ..'A lexikografikus felsorolás négy lépése Pl. 1.1. Írjunk olyan pascal programot, amely az 1.1.2 ábra alapján lexikografikus sorrendben generálja egy halmaz partícióit Most egy másik, a permutációkat lexikografikusan felsoroló módszer mutatunk be. P! egy általános elemének generálásakor az összes N={l,2 ..n} számnak hozzá kell rendelődnie a Pl -hez, majd az N {Pl} -beli összes elemnek hozzá kell rendelődnie a P2-höz, és így tovább. Ezek alapján algoritmusunk szerkezete a következő: Ahhoz, hogy a permutációkat lexikografikus sorrendben kapjuk korlátoznunk kell az egyes Pj -k kiválasztásának sorrendjét. Ha a választható elemek közül elsőként mindig a kisebbiket választ juk, akkor a permutációkat lexikografikusan növekvő sorrendben kapjuk. Kézenfekvő, hogy algoritmusunkban a választható elemeket egy listában tároljuk, a listák kezelése (elem-törlés, -beszúrás, stb.) a pascal nyelvben a jól ismert mutatós módszerrel talán túl sok adminisztrációs lépéssei járna, ezért kihasználva a jelen probléma specifikurnát a megfelelő listát egy tömbbel szimuláljuk, legyen ez a:array[O ..n} o/integer. A tömb egy elemének indexe reprezentálja i-edik listaelem által tárolt értéket, míg maga az elem a lista következő elemére mutat. Vagyis pl. az [1,3,3,4,6,6,0] tömb az 1 ~ 3 ~ 4 ~ 6 listát reprezentálja. 7
  • 9. Ekkor az a[O..n] inicializálását, feltöltését a következő eljárás végzi: procedure Init; var i: integer; begin for i:=O to n-l do a[i):=i+l; a[n):=O; end; Tegyük fel, hogy p[1..n] egy globális tömb var p:array[1 ..n} of integer defmícióval, a permutációk tárolásához, valamint már létezik a PrintPerm eljárás a kész permutációk megjelenítéséhez. Ekkor a permutációkat lexikografikusan felsoroló algoritmus pascal kódja a következő: procedure enum(i:integer); var t:integer; begin t:=Oi while a[t)<> O do begin p[i]:=a[t]; if i<> n then begin a [t] :=a [a [t]]; enum(i+l) ; a [t] : =p [i] ; end else Printperm; t:=a[t]; end; Fig. J. J. 3.: Permutációk lexikografikusan Az algoritmusunknál alkalmazott gondolatmenet, mint majd látni fogjuk ismétléses permutációkra is általánosítható lesz. A fenti algoritmusok teljesítménye között a versenyfeladatok megoldásának szempontjából lényeges különbség nincs. Ezen azt kell érteni, hogy a megoldások a futásra felhasználható idő (1 perc) alatt nagyjából n=ll-ig képesek az összes permutációt előállítani. Természetesen a fenti módszereken kívül számos más módszer is ismeretes, melyek más-más célra használhatók a legalkalmasabban. A különböző algoritmusok több szempontú összehasonlításával foglalkozik Roy [RoyI978] és Ives [Ivesl976]. A probléma (c) részében megfogalmazottakra mind Fike [FikeI975], mind Wells [WeIIsi971] kínál megoldást. A (c)-ben megfogalmazott probléma speciális esete (n=k) annak a problémának amikor egy n elemű halmaz k-ad osztályú kombinációinak összes permutációit rendezzük lexikografikusan és ezek között keressük a i-ediket. Ennek az általánosabb problémának a megoldása a "Kombinációk" című fejezetben található. A (c) problémára ezen kívül hasznos eligazítást találhatunk [BrowI970] -ban is. Irodalomjegyzék az 1.1. fejezethez [FikeI975]: C. T. Fike (1975). A permutation generation method. The Computer Journal, Vol. 18,p21. [Rohll976]: 1. S. Rohl (1976). Programming improvements to Fike's algorithm for generating permutations. The Computer Journal, Vol. 19, p 156. 8
  • 10. [WeIll 1971]: M. B. Wells (1971). Elements ofCombinatorial Computing. Pergamon Press, NewYork [Fisc1912]: L. L. Fischer and K. Chr Krause (1812). Lehrbuch der Combinationslehre und der Arithmetik. Dresden. [Shen1962]: Shen, Mok-Kong (1962). BIT Vol. 2. p. 228. [RoyI978]: M. K. Roy. (1978). The Computer Journal, VoI2l., p. 296. [Ivesl976]: F. M. Ives. (1976).Permutation Enumeration: Four New Permutation Algorithms CACM, Vol. 19., Nr. 2., p. 68. [Brow 1970]: R. M. Brown: Decoding Combinations of the First n Integers Taken k at a Time. CACM Vol. 3-4 p 235. 9
  • 11. 1.2. Ismétléses permutációk A probléma pontos definíciója a következő: Adott 1 <= r <= n pozitív egész (n darab, r különböző elem permutációit keressük), r valamint azF = (ft .t; ···,fr') vektor, ahol n = LJ; és 1::;; J; (i = 1,2, ... r) ;=1 generálandó az M = {u,..,1,2,2, .. ,2, .... ,r,r, .. ,r} ~ ~ '---v---' ft h /, halmaz összes (ismétléses) permutációja. Egy ilyen permutációt jelöljünk csakúgy, mint az előzőekben P = (Pl' P2"" Pn)-vel. A P-t generáló algoritmusunk egybeesik azzal a módszerrel ahogyan "kézzel" felírnánk a fenti permutációkat: Válasszunk M-ből egy elemet az összes lehetséges módon ez lesz Pl' minden egyes ilyen választás után válasszunk egy elemet M{Pl}-ből P2-helyére, ..., és végül minden egyes Pn-l kiválasztása után Pn helyére válasszunk M {Pl' P2"'" Pn-l} -ből, mint az előző fejezetben. Ha r=n akkor az ismétlés nélküli permutációkat kapjuk. Ha az 'összes lehetséges módon' történő választást az elemek növekvő sorrendjében végezzük el, akkor a permutációkat is lexikografikusan ebben a sorrendben kapjuk. Rohl 1978-ban publikálta [Roh1l978] a fenti módszert némi általánosítással: Ha az algoritmus során a kiválasztásokat nem végezzük el csak Ps -ig (S < n )-ig akkor n-elem s-ed osztályú kombinációinak (ismétléses) permutációit (s-permutációit) kapjuk csakúgy, mint az előző fejezetben. Végül algoritmusunk programja a következő: procedure genperm(m,f:vect;r,rO:integer); const max=20; type vect=array[l ..max] of integer; var p:intvect; k:integer; procedure choose(k:integer); var i:integer; begin for i:=l to r do if f[i] <> O then begin p [k] : =m [i] ; dec(f[i]); if k<>rO then choose(k+l) else proc(p); inc(f[i]); end end; begin {genperm} choose(l); end; Fig. 1.2.1.: Rohl algoritmusa (1978). 10
  • 12. Ha valamely n elem ismétléses permutációi közül a lexikografikus sorrendben pontosan az i. -re van sziikségünk akkor az ezt előállító algoritmust Wells [Welli971] munkájában találjuk. Készítsük most el az i-edik lexikografikus ismétléses permutációt generáló algoritmus saját verzióját. Kiindulásként alkalmazzuk Wells az "inverzfeladat"-ot megoldó algoritmusát [We1ll971] . Ez az algoritmus az inputjaként egy permutációból előállítja az adott permutáció lexikografikus sorrendbeli sorszámát: const max=100i type intvect=array[O ..max] of longinti function nalatt k (n:longintik:longint) :longinti Var i :integer i result:longinti Begin result:=li if k<>O then for i:=O to (k-1) do result:=(result div (i+1) )*(n-i)i nalatt k:=result endi function iperm2num(n,r:integerif,p:intvect) :longinti (* osszesen k-1 fele objektumunk van, osszesen n darab objektumunk van, n=f [O]+f [1]+ ...+f [r-1] f[j]: a j. objektumból f[j] darab van O <= j <= r-1 *) var H,MM,J:intvecti q,i,jj:integeri nn,v:longinti begin for i:=O to r-1 do begin h[i] :=OiMM[i] :=Oij[i] :=1 endi for i:=O to n-1 do begin MM[p[i]] :=MM[p[i] ]+n_alatt k(h[p[i]],j [p[i]]) i inc(h[p[i]]) iinc(j [p[i]]) i for q:=O to p[i]-l do inc(h[q])i e nd r v:=liNN:=Oiq:=f[r-1]i for jj:=r-2 downto O do begin NN:=NN+MM[jj]*Vi q:=q+f[jj]; v:=v * n_alatt k(q,f[jj]); end; iperm2num:=NN endi Fig. 1.2.2.: Wellsféle JPERM2NUM foggvény (1971). II
  • 13. A mi feladatunk azonban olyan algoritmus írása, amely a sorszám alapján "legyártja" a hozzá tartozó (ismétléses) permutációt. Vegyük észre, hogy adott M és resetén M bármely MO részhalmazának a lexikografikusan első (jelöljük [MO]F-al) ill. lexikografikusan utolsó permutációja (jelöljük [MO]L-el) egyszeruen megadható az elemek sorbarendezésével. Keressük tehát a M lexikografikusan K-adik permutációját P-t. Próbáljuk megkeresni Pl-et P első betűjét, ekkor PI-re: ipemünumcn -1, r', f', [{MPI }]L ) =< K, ahol f ,[;] .= {fU] - 1, ha j = Pl ,(; .= . . . " 1,2, ...r) (1) f[;], egyébként r' = {r :-1, ha f[PI] =1 r , egyébkén! és nyilván Pl az a szimbólum amelyre iperm2num(n-l,r', f', [{MPI}]L) maximális (1) tulajdonságú. Pl után P2-t mint n-1 darab és r' különböző szimbólum lexikografikus sorrendben vett K - perm2num(n -1, r', f', {M Pl}) sorszámú permutációjának első betűjeként keressük .. és így tovább egészen Pn-ig. Legyen az ezt megvalósító pascal kód megírása ismét az olvasó feladata! P 1.2.1. Írjunk olyan pascal függvényt, amely az előzőek alapján generálja a K. sorszámhoz tartozó lexikografikus permutációját valamely M halmaznak ! Valamely halmaz ismétléses permutációit előállító algoritmust találunk még [Barti967] -ben és [SagI964]-ben is. Kapcsolódó versenyfeladatok: Feladat Instrukció UUSC85-2 Az összes esetek száma (kb. 9!) lehetővé teszi, hogy egyenként megvizsgáljuk őket A megadott öt szám összes permutációit (5!) vizsgáljuk, az összes lehetséges müveletjelezéssel (44). TUBP91-4 Az egyenlő számjegyek elhagyása után az összes esetek száma 10! RUGB92-1 Ismétléses permutációk lexikografikusan RUGB92-4 Mivel a gráf csúcsainak száma nem több mint 8, ezért a csúcsok összes lehetséges sorrendjét megvizsgálva (8!) a minimálisat bizonyosan megtaláljuk ACMF92-2 A hálózatba kapcsolt gépek maximális száma 8, ezért az összes eset vizsgálata KLTE93-3 lehetséges. Irodalomjegyzék az 1.2 fejezethez [Rohll978]: J. S. Rohl. (1978). Generating permutations by choosing. The Computer Journal, Vol 21., p 303. [we1ll971 ]: M. B. Wells (1971). Elements ofCombinatorial Computing. Pergamon Press, New York [Bratl967]: P. Bratley (1967).Permutations with repetitions. CACM Vol. 10. p. 450 [Sag1964]: T. W. Sag (1964) Permutations of set with repetitions. CACM Vol. 7. p 585. 12
  • 14. 1.3. Kombinációk Ebben a fejezetben a feladatunk n-elem r-edosztályú ismétlés nélküli kombinációinak generálása, úgy, a generált kombinációk sorrendje is számít. Ahogy már az ismétlés nélküli permutációkkal foglalkozó fejezetben utaltunk rá, ez a probléma az ismétlés nélküli permutáció generálás általánosításának tekinthető. Most az előző fejezetekkel ellentétben csak a legáltalánosabb eljárást mutatjuk be. Nem foglalkozunk a "rendezettlen" kombinációk generálásával. A lexikografikus algoritmusok közül is csak azzal foglalkozunk, amely a lexikografikusan t. kombinációt fogja közvetlenül előállítani. Keressük praktikusan a Z; = {l, 2, .... , n} halmaz r-edosztályú kombinációinak összes permutációit, vagyis a Pn.r = Pl>P2"'" Pr ,ahol Pl>P2'" .,Pr E {l, 2, ... ,nl és Pi :j:; P, ha i :j:; j. vektorokat. Ezek halmazát jelöljük P(n, r)-el. Nevezzük Pn. r = Pl> P2'"'' Pr -t a rövidség kedvéért el egy r- permutációnak ! Legyen P; r = Pl' P2"'" Pr egy r-permutáció, ekkor Pn. r inverziója a Cn r = C1 , C2,···, Cr vektor, ahol Ci E {O, 1, .... ,n-i} és r-i Ci = Pi - i+ Lo i, j j=l ahol o = {O,hap.<p. J' i.j 1, ha P, > Pi Egy Pn. r-hez tartozó Cn. r előállítása a fenti képIetet követve meglehetősen egyszerű, de kihasználhatjuk a Pn.r és Cn.r kapcsolatának egy speciális tulajdonságát. Nevezetesen, hogy ha Z; elemeit a már l.l-ben megismert listában tároljuk, akkor c1-et úgy kapjuk, hogy megszámláljuk, hogy ebben a listában hány elem előzi meg Pl -et, majd Pl -et töröljük a listából, c2 értékének meghatározásához az így nyert listában meg kell számlálnunk a P2 -t megelőző elemek számát, majd P2 -t is töröljük a listából a többi Ci -t ugyanilyen módszerrel kapjuk: proeedure eodingi var i,t, eount:integeri begini Init; for i:=l to r do begin eount:=O; t:=O; while p[i)<>a[t) do begin t:=a[t); ine(eount) end; eli] :=eounti a [t) :=a [a [t) ) end; Fig. 1.3.1.: Permutációk inverziójánakgenerálása 13
  • 15. A Cn, r ~ Pn, r átalakítást végző eljárás szintén a fenti tulajdonságot használja ki: procedure Decoding; var i,j,t:integer; begin Init; for i:=l to r begin t:=O; for j :=1 to c[i] do t:=a[t]; p [i] : =a [t] ; a [t] :=a [a [t] ]; end end; Fig. 1.3.2.: A C-fP átalakítást végző eljárás. Meg kell még jegyeznünk, hogy lexikograftkusan kisebb r-permutációhoz lexikografikusan kisebb inverzió fog tartozni, vagyis, hogy a Cn,rBPn.r megfeleltetés, ilyen értelemben rendezés tartó. A lexikografikusan t. r-permutációt közvetlenül előállító rPermGen nevű eljárás egyegy-egy értelmű megfeleltetés a Pen, r) és a Z = {l,2, ...,IP(n, r)l} halmazok között. Konstruáljuk meg először rPermGen inverzét, vagyis azt a RankrPerm nevű eljárást amely lexikografikus sorrendben megsorszámozza Z; r-permutációit. RankrPerm konstruálásához felhasználjuk a fent bevezetett inverziók és P(n,r) egy-egy értelmű = kapcsolatát. Cn, r definíciójából látható, hogy egy cp cz, ... , c -vel kezdődő Cn. r CI' Cz, ... , Ci>"" Cr j inverziót pontosan [Pm-i, r-i)1 olyan inverzió előz meg, amelyeknek c ,c Z , •.• ,c előtagjára az igaz, I l j hogy clj=cj j=I,2, .. ,i-Iéscl <c j ugyanis, a c +l végigfut ja a [O,l, ..,n-(i+l)] intervallum j j értékeit, C +Z pedig [O,1,..,n-(i+2)] intervallum on fut végig, és így tovább. Ekkor az így összeszámolt j esetek száma: (n-i)*(n-(i+l))* ... *(n-(r-2))*(n-(r-l))= (n-i)! = IP(n-i,r-i)1 O (n-r)! Vagyis a RankrPerm eljárás a következő lesz: Procedure RankrPerm(c:codeword, var Rank:integer); var i:integer; begin Rank:=l; for i:=l to r do Rank:=Rank+c[i]*P(n-i, r-i); end; Fig. 1.3.3.: A RankrPerm eljárás. A hivatkozott P(n,k) függvény definíciója a pontosság kedvéért a következő: nl P(n,k) := . (n-k)! A RankrPerm eljárás az adott r-permutáció inverziójához rendeli a kívánt sorszámot, vagyis egy adott r-permutáció esetén RankrPerm hívását meg kell előzze a Coding eljárás hívása. Az rPermGen eljáráshoz a tulajdonképpen már l.2-ben is alkalmazott trial-and-error módszer alkalmazásával jutunk. A módszert most is az inverzfüggvényre (a RankrPerm eljárás) alkalmazzuk: 14
  • 16. procedure rPermGen(rank, k:integer; var c:codeword); var i:integer; be gin for i:=n-k downto O do if rank > i*p (n-k, r-k) {Trial} then begin c[k):=i; if k <= r then RankrPerm(rank-i*P(n-k, r-k),k+l); exit; end end; Fig. 1.3.4.: Az rPermGen eljárás. Azonos kiindulás után kissé eltérő gondolatmenetet találunk még [Knot1976]-ban a problémára. Az eredeti problémához kapcsolódó feladatokat találunk még [Welll971] -ben. Kapcsolódó versenyfeladatok: Feladat Instrukció KLTE91-3 KLTE92-1 Lexikografikus kombinációk felsorolása Irodalomjegyzék az 1.3 fejezethez [Knot197 6]: G. D. Knott (1976). A Numbering System for Permutations ofCombinations. Communications of ACM, Vol 19, p 355. [We1ll971]: M. B. Wells (1971). Elements of combinatarial programming. p 130. 15
  • 17. 1.4. Egy halmaz összes részhalmazai, particionálása Tegyük fel, hogy az a feladatunk, hogy egy halmaz (praktikusan az {1,2, ... ,n} halmaz) összes részhalmazait kell generálnunk lexikografikus sorrendben. A fenti halmaz részhalmazainak reprezentációja legyen a következő: Minden egyes részhalmazt jelöljön egy f!. = ~ ~ ... ar számsorozat, úgy, hogy ~ < a2 < ... < ar' ís r :s;n, ahol ~ ~ ... ar az adott részhalmaz elemi. Könnyen látható, hogy ez a jelölés egyértelmű. Definiáljuk most, az egy halmaz részhalmazainak halmazán értelmezett a lexikografikus rendezést: Legyen f!.= ~ ~ ... = ap és f l1 <; ... cq két részhalmaz, akkor ha létezik olyan i (l :s; i :s; q) amelyre minden 1 :s; j :s; i esetén aj = cj és vagy ai < Ci (1) vagy p=i-1 akkor azt mondjuk, hogy ~ lexikografikusan megelőzi (kisebb, mint) c-t. Mindezek alapján pl. n=4 esetén a fenti halmaz részhalmazai lexikografikusan növekvő sorrendben a következők: 1,12,123,1234,124,13,134,14,2,23,234,24,3,34,4 A fenti halmaz részhalmazait n-jegyű bináris számokkal is reprezentálhatjuk: A '1 b2 ••• bn pontosan akkor tartozik az A részhalmazhoz, ha bi = 1 <=> i E A (i = 1,2, .. , n) Vegyük észre, hogya részhalmazokon értelmezett lexikografikus rendezés nem ugyanazt a sorrendet adja, mint az őket reprezentáló bináris számokon (bit-sztringeken) értelmezett lexikografikus rendezés. Ha valamely feladat a részhalmazok felsorolásán kívül, nem követeli meg tőlünk a lexikografikus rendezettséget, akkor az {1,2, ...,n} halmaz részhalmazainak felsorolása bináris reprezentációjukban, nem más, mint az egész számok bináris alakjainak felsorolása. O-től 2n-l-ig. Egy szám bináris alakjának keresésekor kihasználhatjuk, hogy a Pascal a Word típus esetén a számokat éppen a nekünk megfelelő formában tárolja. Egy kicsit bonyolultabb probléma az (l)-el defmiált rendezés szerint a részhalmazok felsorolása, ezt valósítja meg a következő program: Program Subset_enumeration; Const n=13; { <n> elemű halmaz részhalmazait soroljuk föl lexikografikusan A program ebben a sorrendben bármely t.-ediket képes generálni (két részhalmaz akkor különbözik, ha van különböző elemük) A teljesítményről: 486SX25-on n=13 esetén az összes részhalmaz felsorolása kb 40 másodpercet vesz igenybe var t:longint; i,r:integer; b:array[l ..n] of integer; {ha az b[i]>O akkor az i-edik elem a reszhalmazban van} a:array[l ..n] of integer; {az elso r eleme nem mas, mint a t. reszhalmaz } function kettoad(x:integer) :longinti var c:longint;i:integer; be gin c:=l; for i:=l to x do c:=c*2; Kettoad:=c end; 16
  • 18. procedure subset(t:integerivar r:integer)i var k:integerih:longinti begin for i:=l to n do b[i] :=Oi r:=Oi k:=li repeat h:=kettoad(n-k)i if t<= h then begin b[k] :=li inc(r)i arr] :=k endi t:=t-(l-b[k])*h-b[k]i inc (k) until ((k>n) or (t=O)) endi begin for t:=l to kettoad(n) do begin subset(t,r)i writelniwrite(t,' :')ifor i:=l to r do write(a[i],' 'li end end. Fig. 1.4.1.: Az {1,2, .... ,n} halmaz osszes részhalmazának felsorolása. A fenti program az eredeti célkitűzést általánosítva alkalmas arra, hogy az {1,2, ... ,n} halmaz az (1) szerinti rendezésben t-edik részhalmazát generálja. A program subset eljárása hívás után az a, b globális változóban t-edik részhalmazt, az r-paraméterében pedig a t.-részhalmaz elemszámát adja vissza. Legyen Z; = {I, 2, .... , n}. Ekkor a Z; halmaz egy partíciója az azon halmazok P = {7rl' 7r2, ••• , 7rm} halmaza, amelyre 7ri n 7rj = 0, ha i'1':. j és 7r '1':.0, i=I,2, ... ,m 1 (2 ) m és Unt i=l = Z; feladatunk rögzített n esetén az összes fenti tulajdonságú halmazrendszer előállítása. Számítsuk először ki, hányféle különböző particionálása létezik az {1,2, ... ,n} halmaznak ! Jelölje Bn a keresett partíciók számát (Bell-féle szám), ekkor n= 1 esetén nyilván B 1= 1 Tegyük fel, hogy Bl, B2, ••• ,Bn_l ismert. Ekkor Hn_l-hez vegyünk egy a többitől különböző elemet, jelöljük ezt a-val. Keressük meg H; = Hn_ {a} összes partícióit ! l U Világos, hogy azon partíció száma, amelyekben {a} szerepel B n-l' hiszen ezeket úgy nyerhetjük, hogy Hn_l-minden partíciójához hozzávesszük a {a} halmazt. Továbbá az {a, p}-t (p EHn_l) tartalmazó partíciók száma, egy rögzített p -esetén Bn_2, mivel p-t összesen (n~l)-féleképpen választhat juk ki, ezért azon partíciók száma, amelyekben ex, egy kételemű részhalmazba esik 17
  • 19. ( n~l) . Bn_l' és ugyanígy gondolkozva azoknak a partícióknak a száma, ahol a. egy i-elemű részhalmazba esik (~~:). Bn_i Vagyis H; = Hn- 1 u {a} összes partícióinak száma: B n = n~. 1=1 (n-l) 1- n (n-l) n-I 1 B n-m = 1=1 n - 1. B n-l.= k=O L L (n-l) k Bk A táblázat Bn értékeit mutatja: 12 4213597 Fig. 1.4.2.: s; értékei A következőkben M. C. Er 1987-ben publikált [MCER1987] módszerét mutatjuk be. Legyen a C = llC2 ••• cn kódszó a Z; egy partícióját leíró kódszava, úgy, hogy n=4 esetén a következő táblázat mutatja a kódszavakat: Kódszó 1111 1112 1121 1122 1123 1211 1212 1213 1221 1222 1223 1231 1232 1233 1234 Fig. 1.4.3.: Az {1,2,3,4} halmaz összes partíciói és a hozzájuk tartozó kódszavak. A táblázatból is látható, hogy ebben a kódszó konstrukcióban 1 ~ Ci ~ i. Másszóval i E Z; nem kerülhet 1ttbe, ha j > i. A következőkben azt vizsgáljuk, hogy a Z; halmaz összes partícióinak kódszavai hogyan vezethetők le a Zn_l halmaz összes partícióit leíró kódszavak sorozatából hozzáadva cn -t a már meglévő kódszavakhoz. cn értékei az 1,2 ... ,max( ll, C;... Cn-1) + 1 intervallum értékei közül kerülnek ki. Ezen tulajdonságok segítségével működik 4.4 algoritmusunk, amely a megfelelő kódszókat generálja. A hivatkozott SP(m, p) eljárás definíciójában az m = max( eJ, ~ ... Cn_l) és p paraméter adja az aktuális kódszó aktuális jegyét. Eljárásunk feltételezi a PrintPartition eljárást, amely a kódszó B partíció konvertálást ill. az így nyert partíció megjelenítését végzi. Mindezek után eljárásunk a következő: 18
  • 20. type codeword=array[l ..max] of integeri procedure SetPartitions(n:integer)i var c:codewordi procedure SP(m,p:integer)j var i:integeri begin if p > n then PrintPartition(c) else begin for i:=l to m do begin c[p] :=iiSP(m, p+1) i endi c[p] :=m+1i SP(m+1, p+1) end endi begin SP(O, 1) endi Fig. 1.4.4.: M C. Er rekurzív algoritmus az {l.2 ....•n} halmaz összes partíciójának generálásához. A fenti Pascal kód Borland Pascal 7.0-ás verziójú fordítót használva napjaink átlagosnak mondható teljesítményű PC-jén (486 SX 25) a következő futási időeredményeket adta: n Futási idő 10 < 1 sec 11 < 4 sec 12 < 21 sec 13 < 130 sec Fig. 1.4.5.: Az Er-féle rekurzív halmaz particionáló algoritmus futási ideje néhány n-re. Mint a táblázatból is látható, a versenyfeladatok esetén akkor alkalmazható az algoritmus ha feldatbeli n < 13. Ha az eredeti problémát, úgy módosít juk, hogy csupán a lexikografikus sorrendben t. partíciót keressük, akkor a fenti Er-féle algoritmus nem használható hatékonyan, hiszen ahhoz, hogy megkapjuk a a t. partíciót le kell generáltatnunk a megelőző t-l darab partíciót is. A lexikografikusan t. partíciót előállító algoritmust megkaphatjuk, ha megfeleltetést találunk a természetes számok és a már ismertetett konstrukcióval előállított kódszavak között. Az említett megfeleltetés bemutatásához vezessük be a következő jelöléseket: Dn(r, d) :azon CIC2,,,,Cn kódszavak száma, ahol CIC2,,,,Cr rögzített és d = max(c ,cl 2, •. ,cr_l) (r=2, ... ,n+1; d=1, .. ,r-1) Könnyű látni, hogy mivel minden kódszóra igaz, hogy CI = 1 ezért D; (2, 1) = En, továbbá az is igaz, hogy Dn(n+l,d)=l (d=l, 2, ... n) ,valamint Dn(n,d)=d+l (d=l, ... ,n-l). Határozzuk meg D; (n -1, h) -t! 19
  • 21. Ekkor Cl ,C2,··· ,cn-2 ,Cn-l ,Cn '----v------' max=h (1) vagyis cn_l helyére 1,2,,,.,h, h+ 1 kerülhet, ezek közül 1,2,,,.,h úgy, hogy maxfc, ,c2' oo,cn_l) = h igaz marad, vagyis az ilyen kódszavak száma h* Dn(n, h), ha cn-l = h + 1 (azaz cn_l-et is rögzítjük) , akkor az ilyen ( 1) tulajdonságú kódszavak száma Dn (n, h + 1), vagyis összesen: (2) Hasonló okoskodással, kapjuk, hogy Dn(r, d) = d * Dn(r + 1, d) + Dn(r + 1,d + 1) (r = 3,oo,n -1; d = l,oo.,r -1) (3) Vagyis D; (r, d)értékei könnyen kiszámíthatók. Ezek alapján a sorszám ~ kódszó konverzíót végző algoritmust könnyű elkészíteni: procedure rank(t, n:integer; c:codeword)i var r:integer; begin t:=l; d:=l; for r:=2 to n begin do t:=t+([r]-1)*Dn[r+1, d]; if c[r]> d then d:=c[r] end end; Az eljárás feltételezi, hogya Dn[2"n+ 1, 2"n+ 1] tömbben e D; (r, d) megfelelő értékei vannak. Az inverz eljáráshoz, vagyis a kódszó ~ sorszám konverzióhoz, hasonlóan gondolkodva, mint az l.2 fejezetben a következő algoritmust kapjuk: program Set_Partitionsi const Bell:array [0..15] of longint= (1, 1, 2, 5, 15, 52, 203, 877, 4140, 21147, 115975, 678570, 4213597, 27644437, 190899322, 1382958545 )i var n:integerit:longint;r,i,j:bytei Dn:array[1 ..18,1 ..18] of longinti C:codewordi procedure PreCalcD(n:integer); { A D(n) értékek kiszámítása} var d,r:integer; begin Dn [2,1] :=BELL [n] ; for d:=l to n-1 do Dn[n,d] :=d+1i for d:=l to n do Dn[n+1,d] :=1; for r:=n-1 downto 3 do for d:=r-1 downto 1 do Dn[r,d]:= d*Dn[r+1,d] + Dn[r+1,d+1]i 20
  • 22. endi Procedure partition(tO:longint var d:byte)i var r:integeri t,m,k:longinti Begin t:=tO id: =1 i for r:=2 to n do begin m r=O) repeat inc(m) until t <= m * Dn[r+1,d])i if d+1 < m then m:=d+1i c [r] : =mr t:=t-(m-1)*Dn[r+1,d]i if d < m then d:=mi endi endi begin writelni write(' n:')i readln(n)i PreCalcD(n)i c[l] :=li for t:=l to BELL[n] do {a ciklus az összes partíciót generálja} begin partition(t,r)i {a t. partíció elő állítása} for j:=l to r do begin write(j,'. reszhalmaz elemei:')i for i:=l to n do if c[i]=j then write(i, " ')i writeln e nd r end end. A programpartition(t, r) eljárása generálja a lexikografikusan t. kódszót. Az eljárás a kész kódszót a már fent definiált codeword típusú C globális változóba teszi, míg a kódszóban leírt partíció részhalmazainak számát az r-paraméterében adja vissza. A kódszó partícióvá alakítását a fóprogram végzi. A fenti program egy adott n-hez tartozó {1,2, ...,n} halmaz összes partícióját előállítja úgy, hogy végrehajtja a partition(t, r) eljárást a t= 1,2,3, .... ,Bn értékekre. Az összes partíció generálását új algoritmusunk valamivellassabban végzi, viszont cserében a t. partíciót közvetlenül, az ezt megelőző t-I partíció előállítása nélkül állítja elő. A témakörhöz a következő feladat kapcsolódik: KLTE92-7 A probléma neve: Szállítás Mivel a probléma csupán az n<100 korlátozást tartalmazza, ezért az összes eset generálása és ellenőrzése a fenti módszerek bármelyikévei is, nem valószínű, hogy megoldáshoz vezet akkor ha a zsúri teszt inputadataiban a csomagok száma több, mint 13. Az előző korlátozás bevezetésévei 4.4 algoritmusunkkal a csomagok halmazának összes partícióinak előállításával és ellenőrzésévei kiválaszthatjuk az optimálist. Az eredeti feladat megoldásához más módszert kell keresnünk ! További kapcsolódó versenyfeladat: RUGB91-6 21
  • 23. Irodalomjegyzék az 1.4 fejezethez [MCER1987]: M.C. Er (1987) Alghorithm for generating Set Partitions. The Computer Journal Vol. 31 No 3, pp 283 22
  • 24. 1.5. Gray kódok A Gray kódok olyan k-bites bitminták sorozata, amelyek olyan tulajdonságúak, hogy az egymást követőek egymástól egyetlen bitben különböznek. Például a következő 3-bites Gray kódok alkalmasak arra, hogy a O-tói 7-ig a számokat kódolják: Gray kódok Sorszám Decimális érték 000 O O 100 1 4 101 2 5 001 3 1 011 4 3 010 5 2 110 6 6 111 7 7 Fig. 1.5.1.: Gray kódok A Gray kódok között speciális tulajdonságúak a Ciklikus Gray kódok, amelyekre a fentieken kívül az is igaz, hogy az utolsó és az első is egyetlen bitben különbözik egymástól. A fentiek nem Ciklikus Gray kódok. A továbbiakban csak Ciklikus Gray kódokkal foglalkozunk, ezért a rövidség miatt csak Gray kódokként hivatkozunk rájuk. Gray kódok Sorszám Decimális érték 000 O O 001 1 1 011 2 3 010 3 2 110 4 6 111 5 7 101 6 5 100 7 4 Fig. 1.5.2.: Ciklikus Gray kódok Itt megjegyezzük, hogy a Gray kódok O-val (minden bit O) kezdődnek és a következő sza.bállyal generálhatóak: Minden egyes lépésben az adott Gray kódból egyetlen bit negálásával apjuk a következő Gray kódot. Meg kell határoznunk ennek a bitnek a pozícióját. Erre a pozícióra pedig az igaz, hogy pontosan fele annyiszor kel1 változtatni, mint a tőle közvetlenül jobbra ál1ót. Ez a módszer viszonylag könnyen programozható, de megvan az a hátránya, hogyan-edik Gray kódot csak a megelőző n-l kód legyártása után adja. Ismét két problémát fogalmazhatunk meg.: (a) Soroljuk fel az összes k-bites Gray kódot (b) Írjuk fel ak-bites Gray kódok közül az n. et. Természetesen, a (b) probléma az általánosabb. De néha, ha csupán az (a) -problémát kell megoldanunk akkor ha az (a)-t megoldó algoritmusunk egyszerűbb és gyorsabb, mint az általánosabb (b)-t megoldó algoritmusunk, akkor érdemesebb azt használni (lásd például permutáció generálás esetét). Keressünk a fent már leírt módszemél egyszerűbb et az (a) problémához, valamint minél egyszerűbb megoldást a (b)-problémához! Jelöljük G(k) = (go. gp ...• g2k_) -val a k bites Gray kódokat. 23
  • 25. Ekkor G-t a következő rekurzióval defmiálhatjuk: G(1) = (0,1) { G(n + 1) = (Ogo,Ogpo .. ,Og2n_l'Ig _, , ... , Ig ) (1) 2n o Vagyis ezzel egyben módszert adtunk az (a) probléma megoldásához. Az (1) képletből könnyen megkap hatjuk az m-edik Gray kód n. bitjét: g(m, n) = (mmOd2" 2n- <1 ) ( 2) ' Vagyis a (b) problémát akár egy, a (2) formulát használó algoritmussal is megoldhat juk, Legyen D(n) = (ct.,~,... ,d2n), úgy, hogy di gi hányadik biten tér el gi-l -től. akkor D(1) =1 { D(n+ 1) = (D(n),n+ (3) 1,D,ev(n)), ahol D,ev(n):= D(n) jordítottja Ha és n g; = (bn,bn_ p ••• ,'1), hogy i = 'Llj2j (4) j=O akkor b. =/. xor J- J J l, I j = 1,2, .... ,n Ez utóbbi formula teremt kapcsolatot egy szám bináris alakja és a hozzá tartozó Gray kód között.Az ezen a formulán alapuló algoritmusokat megvalósító program oknak megfelelő típusválasztás esetén (Pascal esetén a Word típus ilyen) nem kell tartalmazniuk bináris számmá alakító eljárást, hiszen azt a számítógép változó-értékadáskor elvégzi. Sőt ha észreveszzük, hogya bj-t előállító sor egy bináris l-bittel jobbratolást fed, akkor a programunk a szám bináris alakjából egyetlen utasítással előállíthatja a megfelelő Gray kódot bitmintáját. Mivel a feladatok között találtam olyat, amely megoldásához az (a) ill. (b) problémát is meg kell oldani, ezért a Gray kód generáló algoritmusunkat ebbe ágyazva közöljük: USEC92-1 A probléma neve Bit Twiddler: Származása: 1992 ACM East Central Regional Programming Contest A probléma lényege k-biten (k<=15 ) generálni a Gray kódokat az n.-től az m.-ig, n, m, k a program inputjai. A program outputja a megfelelő Gray kódok decimális alakja:A problémát két részproblémára lehet bontani: (1) k-biten az n-edik Gray kód generálása (2) valamely k-bites Gray kódból kiindulva a következő m darab k-bites Gray kód generálása Mivel a feladat k-ra vonatkozó korlátja (k<=15) elegendően kicsi, ezért használhat juk a már fent emIített pascalbeli Word típu st, valamint a memóriában elfér az összes l5-bites Gray-kód, amely 24
  • 26. tartalmazza (l)-miatt az összes 14, 13, 12, ..,2, l-bites Gray kódot. Az init eljárás generálja a 15- bites Gray kódokat, ahogy ígértük egyetlen értékadással. A program többi eljárása a követelményeknek megfelelő outputot (az adott Gray kód decimális alakját) állítja elő. program Twiddler; uses ert; type gray = array[O ..O] of word; {Csak a cím miatt} var g: Agray; b,j,t,i: word; proeedure init; { A gray kódok előállítása} var i:word; begin for i:=O to maxint do gA[i] := (i xor (i shr 1)); end; proeedure put(tol,ig,biten:word); var i:word; begin for i:=tol to ig do writeln(gA[i]); end; be gin elrseri getmem(g,65535); init; repeat write('Mettől? ');readln(t); write('Meddig? ');readln(i); write('Hány biten? ')ireadln(b); if b=O then halt(O); put(t,i,b-1) until false end. Fig. 1.5.3.: Twiddler.pas 25
  • 27. 2. Geometriai algoritmusok Bevezetés A számítógépeket egyre többen és egyre gyakrabban használják olyan alapvetően geometriai eredetű, nagy mennyiségű adat feldolgozásával kapcsolatos problémák megoldására, mint az alakfelismerés, a térinformatikai, térképészeti vagy háromdimenziós szimulációs problémák. A geometriai algoritmusok szintén nagyon fontosak az olyan komplex fizikai rendszerek tervezésében és a elemzésében, mint például az épületek, autók, gépek és integráltáramkörök. Az ilyen programrendszerekben a tervezők a fizikai objektumoknak megfeleitetett gépi objektumokkal dolgoznak. Ezen gépi objektumok megfelelő szintű számítógépes kezelése, megjelenítése igazán komoly feladat. Az ilyen alkalmazások olyan alapvető objektumai, mint a pontok, szakaszok vagy poligonok és a hozzájuk kapcsolódó alapvető eljárások adják az általában a nehezebbek közé sorolható geometriai versenyfeladatok megoldásának alapjait. A geometriai problémákat könnyen vizualizálhatók, de ez néha nem könnyíti meg a megoldás keresését, inkább csak a probléma megértésében segítenek. Nagyon sok probléma megoldása, amely "szabadkézzel" pillanatok alatt megoldható egy darab papír és ceruza segítségével (pl. eldönteni, hogy egy pont egy poligon belsejében van -e vagy sem) követel egyáltalán nem triviális számítógépes programot. Néhány geometriai versenyfeladat megoldásához elegendő a középiskolai koordinátageometriából tanult alapvető függvények, eljárások (pl. pontok távolsága a síkon, egyenes és pont távolsága, háromszög területe) pontos (le)programozása. Ezek megoldása inkább a szimulációs problémák megoldásához hasonlít. További "geometriai" problémák megoldását tisztán vagy nagyrészt kombinatorikai algoritmusok adják, ezért az ilyen problémák elemzése előtt érdemes a "Kombinatorikai algoritmusok" című fejezetet (még egyszer) áttanulmányozni. A geometriai tartalmú versenyfeladatok harmadik csoportja olyan problémákból áll, amelyek megoldásához az első csoportbeli alapvető geometriai függvények pontos megvalósítása, valamint kombinatorikai alapismeretek szükségesek. A legtöbb algoritmus, amit tanulmányozni fogunk a legegyszerűbb geometriai objektumokkal fog dolgozni a két dimenziós véges, de megfelelően kiterjedt síkon. A legalapvetőbb geometriai objektumot a pontot egy számpárral fogjuk jellemezni (a pont 'koordinátái' a derékszögú koordináta rendszerben). A szakasz reprezentációja egy pontpár, amelyeket az adott szakasz összeköt. A poligon esetünkben pontok listája, és feltételezzük, hogy az egymásután következő pontok szakaszokkal vannak összekötve, valamint, hogy az utolsó pontot az elsővel szintén egy szakasz köti össze, így alkotva egy zárt alakzatot. Az egységesség és az egyszerűség kedvéért rögzítsük le most, hogy hogyan fogjuk ezeket az objektumokat a programjainkban reprezentálni. A poligonok reprezentációja egy tömb. Használható lenne még láncolt lista is, de a listák alapvető műveleteinek programozása a pascalban egy kicsit sok adminisztrációt követel meg a programtói, ezért a tömbök használata a programokat egyszerűbbé teszi. Ha valamely probléma pontoknak egy halmazát érinti, akkor a reprezentációban szintén az array[O ..max] of pont definíciót fogjuk alkalmazni, ahol max valamilyen elegendően nagy szám. Tehát programjaink a következő reprezentációkat fogják használni: type pont = record x :integeri y: integer endi type szakasz = record pl:ponti p2:pont 26
  • 28. endi var poligon: array{O ..max] of ponti Fig. 2. O.J.: Geometriai objektumok reprezentáció ja. Furcsának tűnhet, hogy a pontok a koordinátarendszer rácspontjaira korlátozódnak. A valós reprezentáció ugyanígy használható lenne, de az egész értékeket használata sokkal átláthatóbb és hatékonyabb (az egész számok műveleteit a számítógép sokkal gyorsabban végzi, mint a lebegőpontosakat) programot eredményez, nem beszélve arról, hogy a versenyfeladatok általában megelégszenek az egész értékű geometriai programozással is. 27
  • 29. 2.1. Szakaszok metszete Az első probléma melyet tárgyalni fogunk az egyik legalapvetőbb probléma ezért számos más algoritmus is hivatkozni fog rá, ezért nagyon fontos a precíz kidolgozása .. A probléma a következő: Adott két szakasz, amelyek ponttá is fajulhatnak (kezdő és végpontjuk egybeesik) eldöntendő, hogy metszik-e egymást vagy sem. A következő ábra néhány lehetséges előfordulást mutat: f· Fig. 2.1.1.: Szakaszok a síkon. A kézenfekvő algoritmus esetünkben az, hogy számítsuk ki annak a két egyenesnek a metszéspontját, amelyek tartalmazzák a kérdéses szakaszokat, majd vizsgáljuk meg, hogy a szakaszaink tartalmazzák-e ezt a metszéspontot. Amennyiben a kérdéses egyenesek párhuzamosak, akkor azt kell vizsgálnunk, hogy valamely szakasz végpontja a másik szakaszra esik-e. Az egyenesek metszéspont jának kiszámítása visszavezethető egy kétismeretlenes egyenletrendszer megoldására, amely útmutatást pl. [BELT1989]-ben találhatunk. function metszet (pO,pl,p2,p3: pont) :booleanj var nev,a,b,c,d,x,y:reali flagl,flag2,flag3,flag4:booleanj begin a:=pl.y-pO.Yi b:=pO.x-pl.x; c:=p3.y-p2.Yi d:=p2.x-p3.Xi nev:=a*d-b*ci {az egyenletrendszer matrixanak determinansa} if (abs(a)+abs(b»O) and (abs(c)+abs(d»O) {ha egyik szakasz sem pont} then if nev<>O {nem parhuzamosak} then begin { (x,y) a tartalmazo egyenesek metszespontja } y:=(a*(c*p2.x+d*p2.y)-c*(a*pO.x+b*pO.y))/nevi x:=(d*(a*pO.x+b*pO.y)-b*(c*p2.x+d*p2.y))/nevi flagl:=((pO.x<=x) and (x<=pl.x)) or ((pl.x<=x) and (x<=pO.x)); flag2:=((p2.x<=x) and (x<=p3.x)) or ((p3.x<=x) and (x<=p2.x))j flag3:=((pO.y<=y) and (y<=pl.y)) or ((pl.y<=y) and (y<=pO.Y))j flag4:=((p2.y<=y) and (y<=p3.y)) or ((p3.y<=y) and (y<=p2.Y))j metszet:=flagl and flag2 and flag3 and flag4 end else {a szakaszok parhuzamosak} 28
  • 30. if a*p2.x+b*p2.y=a*pO.x+b*pO.y then begin flagl:=((( (pO.x<=p2.x) and (p2.x<=pl.x) ) or ( (pl.x<=p2.x) and (p2.x<=pO.x) ) )); flag2:=((( (pO.x<=p3.x) and (p3.x<=pl.x) ) or ( (pl.x<=p3.x) and (p3.x<=pO.x) ) )); flag3:=((( (p2.x<=pO.x) and (pO.x<=p3.x) ) or ( (p3.x<=pO.x) and (pO.x<=p2.x) ) )); flag4:=((( (p2.x<=pl.x) and (pl.x<=p3.x) ) or ( (p3.x<=pl.x) and (pl.x<=p3.x) ) )); metszet:= flagl or flag2 or flag3 or flag4; end else metszet:=false else {valamelyik szakasz pont l} if ((abs(a)+abs(b))>O) and (abs(c)+abs(d)=O) {a masodik szakasz pont} then if a*p2.x+b*p2.y=a*pO.x+b*pO.y then metszet:=((pO.x<=p2.x) and (p2.x<=pl.x)) or ((pl.x<=p2.x) and (p2.x<=pO.x)) else metszet:=false else if (abs(a)+abs(b)=O) and (abs(c)+abs(d»O) {az elso szakasz pont} then if c*pl.x+d*pl.y=c*p2.x+d*p2.y then metszet:=((p2.x<=pl.x) and (pl.x<=p3.x)) or ((p3.x<=pl.x) and (pl.x<=p2.x)) else metszet:=false else {mindketto pont} metszet:=(p2.x=p3.x) and (pO.y=p2.y) end; Fig. 2.1.2.,' Szakaszok metszetén ek vizsgálata. Sajnos a túl sok eset vizsgálata és kezelése eléggé bonyolult pascal kódot eredményez. A versenyfeladatok megoldásához nem mindig szükséges a fentihez hasonló, általános algoritmus (pl. ha a szakaszok nem fajulnak pontokká, akkor az algoritmus és vele együtt a kód is lényegesen egyszerűbb). A fuggvény alkalmazhatóságát megnehezíti, hogy a kód nem eléggé átlátható és ezzel együtt nehezen debug-olható, Az algoritmust megvalósító fuggvényt könnyedén át lehet alakítani úgy, hogy amennyiben van metszéspont annak koordinátáit adja is vissza, hiszen pl. nem elfajuló esetben a fuggvény x,y változójában a metszéspont koordinátái előállnak. A fentinél talán egyszerűbb és szintén általános algoritmust mutat be R. Sedgewick [SEDG1988] művében. A közölt algoritmus nem számítja ki explicit módon a szakaszokat tartalmazó egyenesek metszéspontját, hanem a szakaszvégpontok elhelyezkedése alapján dönti el, hogy van-e metszéspont. Sedgewick közli az algoritmus pascal kódját is, de az sajnos nem működik. Viszont az algoritmusnak megvan az az előnye a fentivel szemben, hogy a (helyes) pascal kódja talán kevesebb sorból állna. 29
  • 31. Kapcsolódó problémák: P2.2.1. Adott n szakasz, eldöntendö, hogy zárt poligont alkotnak-e. P2.2.2. Adott n szakasz, eldöntendö, hogy páronként metszik-e egymást. P2.2.3. Hány metszéspont ja van egy adott n csúcsú konvex poligon átlóinak. Irodalomjegyzék a 2.1.1. fejezethez. [SEDG 1988]: Robert Sedgewick: Algorithms. Second Edition. 1988. p. 349. [BELTI989]: Bélteky Károly: Analitikus Geometria és Lineáris Algebra. 1989. 30
  • 32. 2.2. Poligon és pont A versenyeken gyakran tűnnek fel olyan feladatok, melyek arra az alapproblémára vezethetők vissza, amelyben az algoritmusnak el kell döntenie, hogy egy adott pont egy adott (konvex vagy konkáv) poligon belsejében van-e. A kérdést 'emberi' intelligenciával, szabadkézzel meglehetősen egyszerű eldönteni, viszont a gépi megoldás már nem triviális. Az egyik legegyszerűbb algoritmus a következő: l. Ellenőrizzük, hogy a vizsgálandó pont nem esik-e a poligon valamely csúcsára vagy oldalára. Ha igen akkor a pont az adott poligon belső pontja. 2. Keressünk egy olyan pontot a síkon, amely biztosan az adott poligonon kívül van. Mivel a poligont véges sok pont feszíti ki, ezért valamely irányban egy elegendően távoli pont biztosan a poligonon kívülre esik. Legyen ez a pont Q 3. A vizsgálandó pontból (P) húzzunk egy egyenest Q-ba. 4. Vizsgáljuk meg, hogy a PQ egyenesnek van-e közös pontja a poligon csúcsaival, ha igen keressünk egy a mostani Q-tól különböző új külső pontot-o 2. pont 5. Számláljuk meg a PQ egyenes és a poligon oldalainak metszéspontjainak számát. Ha ez páros akkor Papoligonon kívül volt, páratlan esetben P a poligon belső pontja. A 4.pontbeli feltétel az ábrán is látható kivételek miatt kell ellenőriznünk. A programunkban egy adott P-hez a megfelelő Q-t a következő módszerrel keressük meg: 4.1. Megkeressük azt a poligon[i] csúcsot, amely a poligon legjobboldalibb csúcsa 4.2.q.x:= poligon[i] .x+1, q.y:=poligon[i].y 4.3. Ellenőrizzük, hogy Q megfelel-e a fentieknek, ha nem q. y: =q. y+ 1, majd .....-;3.3.pont. Mivel a poligon véges sok csúcsból áll, ezért a 4.3 ciklus véges sokszor (legfeljebb n-szer, ha n a csúcsok száma) fog végrehajtódni, véges sok lépésben megtaláljuk a megfelelő Q pontot. Q Q p ------..---," Fig. 2.2. J.: Pont és Poligon elhelyezkedései a síkon A fenti algoritmus egy lehetséges megvalósítását mutatja a következő program: 31
  • 33. program insidei uses crti const max=20i type pont=record x,y:integer endi var poligon: array[O ..max] of ponti p,q:ponti count,i,n:integeri ctrl, ctr12:booleani procedure InputPoligoni var i:integeri begin clrscri write('n :')ireadln(n)i writeln('-------------------------------------------')i for i:=l to n do begin writeln('Az " i , '-edik csucs koordinatai')i write('x: ')ireadln(poligon[i] .X)i write('y: ')ireadln(poligon[i] .y)i endi poligon[O] :=poligon[n]i endi procedure InputPonti begin writeln('A pont koordinatai')i write('x: ')ireadln(p.x)i write('y: ')ireadln(p.y)i endi begin InputPoligoni InputPonti ctr12:=falsei i:=Oi repeat ctr12:=ctr12 or metszet2(p,p,poligon[i],poligon[i+l])i .i nc t í.j r until (ctr12) or (i=n-l)i if ctr12 then writeln('*8enne') else be gin q.x:=-maxinti for i:=l to n do begin if poligon[i] .x > q.x then begin q.x:=poligon[i] .x+li q.y:=poligon[i].y endi endi repeat q.y:=q.y+li ctrl:=falsei for i:=l to n do ctrl:=ctrl or metszet(poligon[i],poligon[i],p,q)i until not ctrli 32
  • 34. if (p.x = q.x) and (p.y q.y) then writeln('*Kivul') else begin count:=O; for i:=O to n-l do if metszet(poligon[i], poligon[i+l], p,q) then inc(count); if odd(count) then writeln('*Benne ') else writeln('*Kivul '); end end; end. Fig. 2.2.2.,' A fenti program eldönti, hogy egy adott pont egy adott poligon belsejében van e. Az algoritmus egyetlen feltételezést támaszt az input adatokkal szemben, feltételezi, hogy azok a megadott sorrendben egy zárt, önmagát nem metsző alakzat valamilyen rögzített körbejárás szerinti csúcsai. Az alábbi poligonokat algoritmusunk inputjaként meg lehet úgy adni, hogy kielégítsék e feltételt, ezért ezek az esetek is kezelhetők: Sedgewick az előző problémánál hivatkozott könyvében erre a problémára is ad megoldást. Sedgewick algoritmusa csakúgy, mint a fenti, egy félegyenest húz a vizsgálandó pontból egy fiktív, a poligonon garantáltan kívülre eső ponton át, ám bármilyen legyen is ez (mindegy, hogy a poligon csúcsain megy át vagy a poligon valamely oldalát tartalmazza ), ennek a félegyenesnek és a poligonnak számolja össze a metszéspontjait, úgy, hogy közben figyeli az előforduló speciális eseteket, de sajnos a fenti ábrán láthatókhoz hasonló esetekkel nem tud mit kezdeni. Kapcsolódó problémák: P2.2.1. Írjunk hatékony algoritmust, amely eldönti, hogy egy pont egy konvex poligon belsejében van-el P2.2.2. Írjunk olyan pascal függvényt, amely eldönti, hogy két háromszögnek van-e metszéspont ja. Kapcsolódó versenyfeladatok Feladat Instrukció RUGB92-3 Itt csak azt kell vizsgálni, hogy a sík egy adott pontja egy adott háromszög belsejébe esik-e, ezért a feladat jóval egyszerűbb módszerrel is megoldható IOAG91-2 33
  • 35. 2.3. Ponthalmaz konvex burka Ebben a fejezetben egy olyan algoritmust kell készítenünk, amely meghatározza egy adott ponthalmaz konvex burkát. A konvex burok nem más, mint egy konvex poligon, melynek csúcsai az adott ponthalmaz pontjai közül valók és a többi pont a poligon oldalaira vagy belsejébe esik. Fig. 2.3.1.: Ponthalmazok konvex hurka. A pontosság kedvéért egyértelművé kell még tennünk azt az esetek, amikor a ponthalmaz 3 vagy több pontja egy egyenesbe esik a ponthalmaz határán. Ilyenkor algoritmusunk outputját csak a poligon csúcsaira eső pontok 'alkotják. Azaz a 2.3.1 b) ábrán látható ponthalmaz konvex burkát az ABCDEFGH-val megjelölt pontokkal azonosítjuk. A konvex burok egyik tulajdonsága, hogy bármely a poligonon kivüli egyenest a poligon felé mozgatva az a poligont egy csúcsában érinti. Ezt a tulajdonságot alkalmazva a koordináta tengelyekkel párhuzamos egyenesekre kapjuk, hogy a pontok közül azok, amelyek minimális ill. maximális x illetve y koordinátákkal rendelkeznek biztosan a burokhoz fognak tartozni. Ezt a tényt használjuk fel a soron következő algoritmusunk kiindulásaként. Algoritmusunk bemenete pontok egy halmaza, amelyet egy array[J..max] alpont fog reprezentálni. A kimenete pedig egy poligon. Minthogy a poligon típus is egy előbbihez hasonló tömb típus, ezért az algoritmus a bemenetként kapott tömb elemeit egyszeruen átrendezi, úgy, hogy az első M tömbelem fogja a ponthalmaz konvex burkát reprezentálni. Látnunk kell azt, hogy azzal, hogy megtaláltuk és valamilyen módon megjelöltük a ponthalmaz konvex burkát alkotó pontokat, még nem oldottuk meg teljesen a problémát. Az így megjelölt pontokat ugyanis úgy kell sorrendbe rendeznünk, hogy azok valóban poligont alkossanak. Azaz a 2.3.1 b) ábrán látható ponthalmazt inputként megadva a GBCEDFAH output nem elfogadható. Tehát a csúcspontként megjelölt pontokat, még rendeznünk kell. Ha ezen pontok (x, y) derékszögű koordinátáit (r, d) polárkoordinátákká alakítjuk (ahol d az Origótól mért távolság, r pedig a pont helyvektorának valamely rögzített egyenessei vett szöge), majd a pontokat a hozzájuk tartozó r érték alapján rendezzük akkor a kívánt sorrendet kapjuk. Megfontolandó az, hogy az egész probléma nem vezethető-e vissza a ponthalmaz pontjainak (r, d) szerinti lexikografikus rendezésére! Algoritmusunk azonban explicit módon semmilyen rendezést nem fog tartalmazni, lesz azonban egy kódrésze, amely a fent leírtak szerinti legkisebb r értékü pontot választja ki, anélkül, hogy trigonometrikus fuggvényeket használna. A trigonometrikus fuggvények kiküszöbölése az algoritmusból a megvalósított programot gyorsabbá, hatékonyabbá teszi. Az algoritmus: 1 Válasszuk ki a minimális y koordinátájú pontok közül azt amelynek a legkisebb x koordinátája van.. A fentiek alapján ez a pont biztosan a konvex burok csúcspontja lesz. Legyen ez a burok első pontja. 2 A burok második pontját úgy kaphat juk, hogy a burok első pontján át húzunk egy x- tengely jel párhuzamos egyenest (az előző pont garantálja, hogy ezen egyenes alatt, már nincsenek pontok), majd ezt az egyenest forgassuk az előző pont körül az óramutató 34
  • 36. járásával ellentétes irányban addig, amíg valamely pontba ütközünk. Ha egyszerre több pontba ütköztünk akkor válasszuk ki a távolabbi pontot, és ez lesz a burok következő pontja. 3. A burok következő pontját úgy kapjuk, hogy egyenesünket az utoljára burokpontként megjelölt pont körül forgatjuk az eddigiekkel megegyező irányban addig amíg valamely pontba nem ütközünk. Ha egyszerre több pontot is elért az egyenesünk, akkor mindig a távolabbi pontot választ juk a burok következő pontjaként. 4. Ismételjük a 3.-beli eljárást addig míg a kiindulási pontunkat el nem értük. Fig. 2.3.2.: Ponthalmaz konvex burkát előállító algoritmus. Az algoritmusbeli feltételek garantálják, hogy a burokbeli pontokat a helyes sorrendben állítjuk elő. Az algoritmust megvalósító pascal program technikai megoldása az, hogy az így megjelölt pontokat az input tömb elejére cseréli és a már beválasztott pontokat nem vizsgálja újra. A következő ábra az algoritmus működését szemlélteti: 1 1 4 3 3 1 1 4 4 5 5 3 3 1 1 Fig. 2.3.3.: A "csomagoló" eljárás működése. Visszatérve a konvex burok értelmezésére, megadására, az algoritmust és így a programot is egyszeruen át lehet alakítani, úgy, hogy az a burok csúcsoktói különböző pontjait is előállítsa 35
  • 37. outputként. Egyszerűen akkor, amikor egyenesünk több ponttal ütközik egyszerre, akkor az érintett pontok közül a legközelebbit kell a burok következő csúcspontjaként megjelölnünk. Mindezek után az algoritmust megvalósító pascal program: program becsomagoli const max=100i type pont=record x:integeri y:integer erid r var inp,out:texti ok,s:stringi n,i,j,k:integeri poligon = array[O ..max] of ponti procedure str_to_pont(s:stringivar q:pont)i var v,j,xl,x2:integeri sl:stringi begin v:=pos(',',s)i sl:=copy(s,l,v-l)i delete(s,l,v)i val(sl,xl,j)i val(s,x2,j)j q.x:=xli q.y:=x2i e nd r function theta(pl,p2:pont) :reali var dx,dy,ax,ay:integeri t:reali be gin dx:=p2.x-pl.xiax:=abs(dx)i dy:=p2.y-pl·Yiay:=abs(dY)i if (dx=O) and (dy=O) then t:=-l else t:=dy/(ax+aY)i if dx<O then t:=2-t else if dy<O then t:=4+t; theta:=t*90.00 endi function d(pl,p2:pont) :reali var dx,dy:reali begin dx:=p2.x-pl.Xi dy:=p2.y-pl·Yi d:=sqrt(dx*dx+dy*dy) endi function csomag (c:integer) :integeri var i,min,m:integeri minszog,v,mintav,th:reali t:ponti begin min:=li 36
  • 38. for i:=2 to n do if poligon[i] .y<poligon[min].y then min:=i else if poligon[i] .y=poligon [min] .y then if poligon[i] .x<poligon[min].x then min:=i; m:=O; poligon[n+l] :=poligon[min]; minszog:=-l; repeat inc (m) ; t:=poligon[m];poligon[m] :=poligon[min];poligon[min] :=t; min:=n+l; v:=minszog; minszog:=360.00imintav:=O; for i:=m+l to n+l do begin th:=theta(poligon[m],poligon[i])i if th >= v then if th < minszog then begin min:=ii minszog:=th;mintav:=d(poligon[m],poligon[min]) end else if th minszog then if c*d(poligon[m],poligon[i]) > c*mintav then begin min:=i; minszog:=thi mintav:=d(poligon[m),poligon[min)) end end until min=n+l; csomag:=m end; begin assign(inp, 'pontok.inp'); assign(out, 'burok.out'); reset(inp); rewrite(out); repeat readln(inp,s); n:=O; while s<>'*' do begin inc (n); str_to_pont(s,poligon[n))i readln(inp,s) end; for i:=l to csomag(-l) do writeln(out,poligon[i) .x,',' ,poligon[i) .y) i writeln (out, '*') until eof(inp)i close(inp);close(out); end. 37
  • 39. Fig. 2.3.4.: A Konvex Burok algoritmus. A program a 'pontok imp' nevű imput állományból olvassa be a ponthalmazokat, és a 'burok.out' nevű output állományba írja a megfelelő konvex burok csúcspontjait. Az állományok szerkezete a következő: Az input állomány blokkokból áll, egy blokk egy ponthalmaz pontjainak megadását tartalmazza, úgy, hogy egy sorban pontosan egy pont kordinátái találhatók vesszővel elválasztva. A blokk végét '*' jelzi. Az output állomány az inputként megadott ponthalmazok konvex burkát tartalmazza . Egy sorban pontosan egy pont koordinátái vesszővel elválasztva. A burok végét egy új sorban egy '*' jelzi. A program csomag(c:integer) eljárása végzi a fent jellemzett eljárást. A c:integer paraméter értékétől függ, hogy az output állományba, melyik megadás szerint kerüljenek a poligon pontjai. c > O esetén, csak a poligon csúcspontjai, c < O esetén azon pontok is, amelyek a poligon oldalaira esnek A probléma általánosításának egyik lehetséges módja a több dimenziós térre történő kiterjesztése. Könnyű belátni, hogy a fenti algoritmus több dimenziós térben is helyesen működik, ha elkészítjük a fenti pascal program több dimenziós, hipersíkokat is kezelő változatát. Kapcsolódó Problémák: P2.3.l. Írjunk olyan algoritmust, amely online módon adja meg az inputként megadott ponthalmaz konvex burkát. Az input pontokat egymás után egyesével adjuk meg az eljárásunknak és az eddig megadott pontok konvex burkát adja meg lépésenként, módosítva azt minden újabb pont után ha szükséges. lásd [SHAM1977] Kapcsolódó versenyfeladatok: Feladat Instrukció IOAG91-2 A feladat megoldása mindkét eddig ismertetett algoritmus alkalmazását megköveteli ACMF92-4 A konvex burok algoritmus explicit alkalmazása. KLTE93-2 Irodalomjegyzék a 2.3 fejezethez [SHAM1977]: Shamos, M. 1. 1977, Computational geometry [GRAHI972]: Graham, R. L. An efficent algorithm for determining the convex hull ofa fmite planar set. Inform. Processing Letters 1 1972. P 132-133 [PREP 1977]: Prepatra, F. P., Hong S. J.: Convex hulls of fmite sets in two and three dimensions. Communications of ACM, Vol 20. Num 2., p 87-93 38
  • 40. 3. Gráfalgoritmusok Bevezetés A programozói versenyek "nehezebb" feladatainak legnagyobb hányadát a gráfelméleti feladatok teszik ki. Ezek a feladatok lehetnek expliciten gráf elméleti fogalmakkal megfogalmazottak (olyanokkal, mint feszítőfa vagy legrövidebb út), vagy valamilyen a mindennapi életből vett köntösbe bújtatottak. A gráf, mint matematikai objektum nem más, mint csúcsok és élek véges halmaza. A csúcsok a legkülönfélébb más objektumok lehetnek, egyetlen kikötésünk, hogy valamilyen megkülönböztetésük létezzen: legyen nevük vagy más azonosítójuk. A gráf egy éle összeköttetést, kapcsolatot reprezentál a gráf pontosan két csúcsa között. ® A fenti ábrán látható ABCDF, G, EHI csúcspontokkal jellemzett rajzok alkothatnak egy kettő vagy három gráfot. Tekinthetjük például ABCDFG-t egy gráfnak. Ezt a gráfot definiálhat juk úgy, hogy felsoroljuk a csúcsait: A, B, C, D, F, G majd az éleit AF, AD, BC, BD, Bf, CD. Egy X csúcspontból pontosan akkor vezet út egy Y csúcsba egy gráfban, ha létezik a csúcsoknak egy olyan listája, amelyben az egymás után következő csúcsok a gráfban éllel vannak összekötve. A gráf összefüggő minden csúcsából létezik út minden más csúcsába. Pl a fenti ABCDF gráf összefüggő, míg az ABCDFEHI gráf nem összefüggő. A gráf egy egyszerű útja azon út amelyhez tartozó listában nincs ismétlődő csúcspont. A gráf egy kör-e azon egyszerű út, amelyben a kezdő és a végpont megegyezik (vagyis a listában egyetlen ismétlődés van). Azt a gráfot, amelyben egyetlen kör sincs és összefüggő fá-nak nevezzük (a nem összefüggő, körmentes gráf ot erdő-nek nevezzük). A gráf feszítőfá-ja a gráf azon részgráfja, amely a gráf összes csúcsát tartalmazza, és pontosan annyi élt a gráf élei közül, hogy az így nyert részgráf összefüggő legyen. Könnyen látható, hogy n csúcsú gráf feszítőfájának pontosan n-l éle van. A feszítőfa másik ismert tulajdonsága, hogy bármely új élt hozzáadva már kört kapunk. Azt a gráf ot, amely rendelkezik az összes lehetséges éllel teljes gráf- nak, amelyik viszonylag kevés éllel rendelkezik ritka gráf-nak, amelyik elég sok éllel rendelkezik sűrű gráf-nak nevezzük. A gráf ok gépi reprezentációiról nagyon hasznos dolgokat tudhatunk meg [SEDGI988]-ban valamint [NIE 11977] -ben. A gráf algoritmusok bonyolultsága (itt most bonyolultságon az adott körülmények közötti legrövidebb idejű megvalósíthatóságot kell érteni, ennek egyik leglényegesebb komponense a kód rövidsége és egyszerűsége) nagyban függ attól, hogy a gráfok milyen reprezentációját választottuk. Általában a legegyszerűbb kódot azon algoritmusok eredményezik, amelyekben a gráf reprezentációjaként valamilyen mátrixos (szomszédsági vagy összefüggőségi) megoldást választottunk. Persze vannak problémák, amelyeknél a listás reprezentáció elkerülése sokkal "költségesebb" lenne, mint annak megvalósítása. Irodalomjegyzék a 3. fejezethez [SEDGI988]: Robert Sedgewick: Algorithms pp418 - 421 [NIEI1977]: J. Nievergelt, J. C. Farrar, E. M. Reingold: Matematikai Problémák Megoldásainak Számítógépes Módszerei. 72-73 oldal 39
  • 41. 3.1. Mélységi keresés Az egyik legklasszikusabb gráf elméleti algoritmus a keresés. Ebben a problémában olyan kérdésekre kell az algoritmusnak válaszolnia, mint van-e az adott gráfban kör, vagy melyek a gráf összefüggő komponensei. Fogalmazzuk meg először, mit is kell most keresésen értenünk! Legyen adott a gráfnak két csúcsa, nevezetesen START és GOAL, a feladat megkeresni a STARTból a GOALba vezető utat, vagyis azt a listát amelynek első elem START utolsó eleme pedig GOAL és az egymás után következő listaelemeket a gráfban él köti össze. l. Meg kell vizsgálnunk, hogya START csúcs megegyezik-e a GOAL-al, ha igen, akkor kereső eljárásunk már véget is ért hiszen a keresett listának egyetlen elemből áll: a ST ART csúcsból. 2. Ha nem vagyunk ilyen "szerencsések", akkor keressük meg a START csúcs összes szomszédját, ezt a folyamatot kiterjesztésnek nevezzük. Az így nyert csúcsokat a START csúcs "gyermekeinek", "utódjainak" vagy "rákövetkezőinek" nevezzük. Ennek a lépésnek az eredménye egy lista. 3. Eztán a fenti két lépést alkalmazzuk az így nyert új csúcsokra, úgy, hogya 2. lépés után az újabb listát az előző listához fűzzük. p.z S1 csúcs kiterjesztése Fig. 3.1.1.: Altalános kereső eljárás működése A módszerek különbözőségének okát abban kell keresnünk, hogy a 2. lépés után kapott csúcsokat milyen sorrendben terjesztjük ki. A két legismertebb kereső stratégia a szélességi és a mélységi keresés. A mélységi keresés alkalmával mindig olyan "messze" haladunk a gráfban a START csúcstól, amilyen messze csak lehet, vagyis a fenti ábrán mélységi stratégia szerint az S II csúcs következne kiterjesztésre, szélességi stratégia szerint pedig az S2. De mindennél többet mond talán az algoritmus formális leírása. INPUT: A gráf két csúcsa START és GOAL FELADAT: Meghatározni, hogy létezik-e egyszerű út START és GOAL között. OUTPUT: Igen vagy Nem 1. Legyen NYILTAK egy lista, első eleme legyen START Legyen ZÁRT szintén egy üres lista 2. Ha NYILTAK üres, akkor algoritmus vége és a válasz Nem 3. Vedd az első csúcsot a NYILTAK-ból legyen ez A. Szúrd A-t a ZÁRT lista elejére. a) Ha A = GOAL akkor az algoritmus vége és a válasz Igen. b) Hajtsd végre a kiterjesztést A-ra, ennek eredménye legyen az UTÓDOK listája. b1. Vedd ki az UTÓDOK listájából azon csúcsokat, melyek szerepelnek a ZÁRT listában b2. Fűzd az UTÓDOK listáját a NYILTAK listája elé. b3 Menj 2. -re Fig. 3.1. 2.: A mélységi keresés algoritmusa. 40
  • 42. A fenti algoritmus, nem használja ki az élek reflexivitását, vagyis irányított gráf okra is tökéletesen működik. A mélységi keresési stratégiát természetesen nem csak arra használhatjuk, hogy megkeressük két csúcs között egy gráfbeli utat. Használhatjuk pl. arra is, hogy megtudjuk, hogy egy adott gráf tartalmaz-e kört. Ugyanis ha a 3bl-ben ténylegesen ki kell vennünk valamely csúcsot a ZÁRT listából, akkor ez azt jelenti, hogy ehhez a csúcshoz egy újabb útvonalon eljutottunk, vagyis, hogy a gráf kört tartalmaz. Azt is könnyen beláthatjuk, hogy a stratégia segítségével leválogathatjuk egy gráf összefiiggő komponenseit: Meg kell jelölnünk a kiterjesztett csúcsokat és ha a NYILTAK listája már üres és még van a gráfban meg nem jelölt csúcs, akkor ez azt jelenti, hogy ez a meg nem jelölt csúcs a gráf eddigitől különböző komponensében van. A témával kapcsolatban nincs már más hátra, mint a fenti ellenőrzéseket elvégző pascal program: program deptfirsti { input formatum (a graf megadasa) 1. sor csucsok szama (v) 2. sor elek szama (e) 2+1. sor elso ,1: a ket csucs sorszama amelyeket osszekot [space]-szel elvalasztva. 2+2. sor masodik,l 2+e sor e-edik ,1 const maxV=200; type link=Anode; node=record v :integer; next :link end; var inp,out:text; id,k,l,v,e,last:integer; components:integer; {ennyi osszefuggo komponens} graph: array[l ..MaxV] of link; order: array[l ..MaxV] of integer; cycle:boolean {=true ha van benne kor }; procedure Str2Vertex(s:string;var k,j:integer); var v,h:integer; xl,x2:integer; sl:string; begin v:=pos(' ',s); sl:=copy(s,l,v-l); delete(s,l,v); val(sl,k,h); val(s,j,h) end; procedure ReadInput; var j,i,x,y:integer; s:string; t:link; begin assign(inp, 'deptlst.inp'); reset(inp); readln(inp,v); 41
  • 43. readln(inp,e); for i:=l to v do graph[i] :=NIL; for j:=l to e do begin readln(inp,s);Str2Vertex(s,x,y); new(t);tA.v:=x; tA.next:=graph[y];graph[y] :=t; new(t);tA.v:=y; tA.next:=graph[x];graph[x] :=t; end; end; Procedure visit(k:integer); var t:link; begin inc(id);order[k] :=id; t:=graph[k]; while t<>NIL do begin if order[tA.v]=O then visit(tA.v) else cycle:=true; t:=tA.next end end; Procedure Conclusion; begin writeln(out, 'Osszefuggo komponensek: ',components); {/ A /} if cycle then writeln('kor :van ') else if components=l then writeln('fa') else writeln('erdo'); writeln(out) end; begin assign(out, 'deptlst.out'); rewrite(out); ReadInput; id:=O; last:=O; components:=O; for k:=l to v do order[k] :=0; for k:=l to v do begin last:=idi if order[k]=O then begin visit(k); inc(components); writeln(components, osszefuggo reszgraf:'); for 1:=1 to v do if (order[l]<=id) and (order[l]>last) then write(l, I ')i writeln; end end; Conclusion; close(out) end. Fig. 3.1.3.: A mélységi keresés. A program feltételezi, hogy az input fileban megadott csúcsok és élek egyetlen gráfhoz tartoznak. A program az előzőek szerinti stratégiát használva alkalmas: -a gráf bejárására (minden csúcsot pontosan egyszer meglátogat) - a gráf összefüggő komponenseinek leválogatására 42
  • 44. - eldönti egy gráfról, hogy van-e benne kör; (nem sorolja fel a köröket) - ezek alapján eldönti, hogy a gráf fa ill erdő-e. A gráf reprezentációja most egy tömb. A tömb elemei listák, minden csúcshoz pontosan egy. a lista elemei az adott csúcs szomszédjai. Vagyis a fenti gráfot programunkban a következő tömb reprezentálja: graph[1]= 6 -> 4 graph[2]= 3 -> 4 -> 6 graph[3]= 2 -> 4 graph[4]= 2 -> 3 graph[5]= 8 -> 9 graph[6]= 1 -> 2 graph[7]= graph[8]= 5 -> 9 graph[9]= 5 -> 8 Könnyű látni, hogy ez a reprezentáció alkalmas irányított gráf ok gépi reprezentálására is. Irányított gráfok esetén egy adott élt, csak egyszer kell szerepeltetni a struktúrában. Az order[l..MaxVJ tömb tartalmazza a gráf bejárási sorrendjét, kezdetben minden eleme O. A gráf azon összefuggő komponensének mélységi bejárását, amely a paraméterként megadott csúcsot tartalmazza a rekurzívan működő visít eljárás végzi. A visít eljárást (a kiterjesztést) a l-es csúcsra végrehajtva az eljárás megkeresi az l-es csúcs szomszédjai közül, azt amely még nem volt megjelölve az order tömb segítségével. Ha talál, ilyet akkor erre végrehajtja a visít eljárást, ha nem talál ilyet akkor ez azt jelenti, hogy, az adott csúcs minden szomszédját bejártuk, vagyis vissza kell lépnünk egy szinttel "feljebb" (backtrack) .... Egy gráf összefüggő komponenseinek leválogatására alkalmas Warshall algoritmusa is [NIE21977] Kapcsolódó versenyfeladatok Feladat Instrukció KLTE91-5 Megoldható más, nem gráf elméleti módszerrel is. A gráf os módszer megvalósítása hosszabb és bonyolultabb módszert követel. IOAG91-4 A feladatban egy gráf legtöbb csúcsot tartalmazó összefuggő komponensét kell megkeresnünk, ez a fenti program segítségével lehetséges. Mivel a feladatban a gráf a szomszédsági mátrixával adott, ezért Warshall algoritmusa is nagyon jól alkalmazható. ACMF91-1 Útkeresés. KLTE92-3 Körkeresés KLTE92-5 Útkeresés. USEC92-3 Útkeresés. TUBP92-5 Összefuggő komponensek szétválasztása. ACMF92-5 Vegyünk sorra minden élt a feladat irányított gráfjában. Majd az adott élt hagyjuk el az eredeti gráfból . Ha ebben a gráfban létezik út az adott él végpontjai között akkor az adott él által reprezentált függőség redundáns. Irodalomjegyzék a 3.1 fejezethez [NIE21977]: J. Nievergelt, J. C. Farrar, E. M. Reingold: Matematikai Problémák Megoldásainak Számítógépes Módszerei. 77 - 78 oldal. 43