const Presnost = 1E-6; {Přesnost pro práci s reálnými čísly} {Čísla lišící se o méně než tuto konstantu považujeme za stejná} type PPocitac = ^TPocitac; TPocitac = record X,Y:real; Poradi:integer; Dalsi:PPocitac; end; var Pocitace:PPocitac; Nejlevejsi,Nejpravejsi:PPocitac; NadSpojnici,PodSpojnici,NaSpojnici:PPocitac; procedure Nacti; {Načtení vstupu a určení nejlevejšího a nejpravějšího počítače (z pohledu X)} var N,i:integer; Novy:PPocitac; begin readln(N); {počet počítačů} Pocitace:=nil; if N <> 0 then begin {načtení do lineárního spojového seznamu} new(Novy); readln(Novy^.X,Novy^.Y); Novy^.Poradi:=1; Novy^.Dalsi:=nil; Nejlevejsi:=Novy; Nejpravejsi:=Novy; {první počítač je zvlášť kvůli inicializaci} Pocitace:=Novy; for i:=2 to N do begin new(Novy); readln(Novy^.X,Novy^.Y); if (Novy^.X > Nejpravejsi^.X) or ((Novy^.X = Nejpravejsi^.X) and (Novy^.Y < Nejpravejsi^.Y)) then Nejpravejsi:=Novy; if (Novy^.X < Nejlevejsi^.X) or ((Novy^.X = Nejlevejsi^.X) and (Novy^.Y > Nejlevejsi^.Y)) then Nejlevejsi:=Novy; Novy^.Poradi:=i; Novy^.Dalsi:=Pocitace; Pocitace:=Novy; end; end; end; procedure Rozdel; {Rozdělí počítače podle relativní polohy vzhledem k spojnici nejlevějšího a nejpravějšího} var Zpracovavany:PPocitac; ZSlozkaVektorovehoSoucinu:real; VektX,VektY:real; {vektor popisující směr spojnice nejlevějšího a nejpravějšího počítače} ZpracVektX,ZpracVektY:real; {vektor popisující směr spojnice nejlevějšího a zpracovávaného bodu} begin NadSpojnici:=nil; PodSpojnici:=nil; NaSpojnici:=nil; VektX:=Nejpravejsi^.X - Nejlevejsi^.X; VektY:=Nejpravejsi^.Y - Nejlevejsi^.Y; while Pocitace <> nil do begin Zpracovavany:=Pocitace; Pocitace:=Pocitace^.Dalsi; {vyřazení zpracovávaného počítače ze seznamu} if (Zpracovavany = Nejlevejsi) or (Zpracovavany = Nejpravejsi) then continue; {tyto 2 se zpracovávají zvlášť} ZpracVektX:=Zpracovavany^.X - Nejlevejsi^.X; ZpracVektY:=Zpracovavany^.Y - Nejlevejsi^.Y; ZSlozkaVektorovehoSoucinu:=VektX * ZpracVektY - VektY * ZpracVektX; if abs(ZSlozkaVektorovehoSoucinu) < Presnost then begin {leží na spojnici} Zpracovavany^.Dalsi:=NaSpojnici; NaSpojnici:=Zpracovavany; end else if ZSlozkaVektorovehoSoucinu > 0 then begin {leží nad spojnicí} Zpracovavany^.Dalsi:=NadSpojnici; NadSpojnici:=Zpracovavany; end else begin {leží pod spojnicí} Zpracovavany^.Dalsi:=PodSpojnici; PodSpojnici:=Zpracovavany; end; end; end; function Merge(Seznam1,Seznam2:PPocitac):PPocitac; {dva setřízené seznamy slije - původní seznamy jsou během procesu zničeny} var Prvni,Posledni:PPocitac; function Porovnej(Pocitac1,Pocitac2:PPocitac):boolean; {porovná polohy dvou počítačů a vrací true, pokud má být Pocitac1 zatřízen jako první} begin Porovnej:=(Pocitac1^.X < Pocitac2^.X) or ((Pocitac1^.X = Pocitac2^.X) and (Pocitac1^.Y > Pocitac2^.Y)); {zde si můžeme dovolit mezi reálnými čísly test na rovnost - zaokrouhlovací chyby, které vzniknou} {při načítání budou u stejných hodnot na vstupu stejné a tedy rovnost bude doopravdy platit} end; begin if (Seznam1 = nil) then Merge:=Seznam2 {v případě, že je jedna posloupnost prázdná je slití triviální} else if (Seznam2 = nil) then Merge:=Seznam1 else begin if Porovnej(Seznam1,Seznam2) then begin {nejdříve zjistíme čím bude výsledná posloupnost začínat} Prvni:=Seznam1; Seznam1:=Seznam1^.Dalsi; end else begin Prvni:=Seznam2; Seznam2:=Seznam2^.Dalsi; end; {Konec funkce (až po přiřazení výsledku) jen slije zbytek seznamů.} {Je zde implementována nerekurzivní varianta. Rekurzivní by byla výrazně kratší.} { Prvni^.Dalsi:=Merge(Seznam1,Seznam2); } Posledni:=Prvni; while (Seznam1<>nil) and (Seznam2<>nil) do begin {a pak slijeme zbytek} if Porovnej(Seznam1,Seznam2) then begin Posledni^.Dalsi:=Seznam1; Posledni:=Seznam1; Seznam1:=Seznam1^.Dalsi; end else begin Posledni^.Dalsi:=Seznam2; Posledni:=Seznam2; Seznam2:=Seznam2^.Dalsi; end; end; if (Seznam1 = nil) then Posledni^.Dalsi:=Seznam2 else Posledni^.Dalsi:=Seznam1; Merge:=Prvni; end; end; function MergeSort(Seznam:PPocitac):PPocitac; {dostane seznam a vrátí ho setřízený} var Seznam1,Seznam2,Swap:PPocitac; begin if (Seznam = nil) then MergeSort:=nil else if (Seznam^.Dalsi = nil) then MergeSort:=Seznam {koncové podmínky} else begin Seznam1:=nil;Seznam2:=nil; while Seznam<>nil do begin {rozdělí seznam poloviny - sudé a liché prvky zvlášť} Swap:=Seznam^.Dalsi; Seznam^.Dalsi:=Seznam1; Seznam1:=Seznam2; Seznam2:=Seznam; Seznam:=Swap; end; Seznam1:=MergeSort(Seznam1); {setřízení polovin} Seznam2:=MergeSort(Seznam2); MergeSort:=Merge(Seznam1,Seznam2); {a merge setřízených posloupností} end; end; procedure Vypis(Seznam:PPocitac); begin while (Seznam<>nil) do begin write(Seznam^.Poradi,' '); Seznam:=Seznam^.Dalsi; end; end; function Obrat(Seznam:PPocitac):PPocitac; var ObracenySeznam,Dalsi:PPocitac; begin ObracenySeznam:=nil; while Seznam<>nil do begin Dalsi:=Seznam^.Dalsi; Seznam^.Dalsi:=ObracenySeznam; ObracenySeznam:=Seznam; Seznam:=Dalsi; end; Obrat:=ObracenySeznam; end; begin Nacti; if Pocitace = nil then begin writeln; {není co vypisovat - 0 počítačů} end else if Pocitace^.Dalsi = nil then begin writeln('1'); {jedinný počítač ... takže není příliš co řešit} end else begin Rozdel; if (NadSpojnici = nil) and (PodSpojnici = nil) then writeln('Řešení neexistuje.') {všechny počítače leží na spojnici} else begin NadSpojnici:=MergeSort(NadSpojnici); {setřízení jednotlivých seznamů} PodSpojnici:=MergeSort(PodSpojnici); NaSpojnici:=MergeSort(NaSpojnici); if (NadSpojnici = nil) then NadSpojnici:=NaSpojnici else PodSpojnici:=Merge(NaSpojnici,PodSpojnici); {přidání bodů na spojnici k příslušné straně} write(Nejlevejsi^.Poradi,' '); Vypis(NadSpojnici); write(Nejpravejsi^.Poradi,' '); PodSpojnici:=Obrat(PodSpojnici); Vypis(PodSpojnici); writeln; end; end; end.