program BludisteKlice; const MaxSq = 1000000; Keys = 4; KeyCn = 1 shl Keys; { Počet kombinací klíčů } KeyMask = (1 shl Keys) - 1; { = 1111 dvojkově } { Následující konstanty definujeme tak, aby platilo: } { Libovolný klíč < Exit < Prázdné políčko < Překážka < Dveře } sqKeyRed = 1; { = 0001 (klíče) } sqKeyGreen = 2; { = 0010 } sqKeyBlue = 4; { = 0100 } sqKeyMagenta = 8; { = 1000 } sqExit = 10; { východ } sqEmpty = 11; { prázdné políčko } sqBlock = 12; { zeď } sqDoorRed = 33; { = 100001 (dveře) } sqDoorGreen = 34; { = 100010 } sqDoorBlue = 36; { = 100100 } sqDoorMagenta = 40; { = 101000 } availMovesCnt = 4; availMoves: array[1..availMovesCnt, 1..2] of Shortint = ((-1, 0), (1, 0), (0, -1), (0, 1)); availMovesName: array[1..availMovesCnt] of Char = ('Z','V','S','J'); var plan: array[0..MaxSq-1] of Byte; r, s: LongInt; startX, startY: LongInt; fromDir: array[0..MaxSq-1] of Cardinal; { dvojice bitů pro každou kombinaci } { Směr, ze kterého jsme na dané políčko přišli } fromDifCn: array[0..MaxSq-1] of Word; { jeden bit pro kombinaci } { Přešli jsme z jedné kombinace klíčů do jiné? } used: array[0..MaxSq-1] of Word; { jeden bit pro kombinaci } { Byli jsme již na tomto políčku? } queueI: array[0..MaxSq * KeyCn - 1] of Longint; queueCn: array[0..MaxSq * KeyCn - 1] of Byte; qCount: Longint; qPos: Longint; endIndex: Longint; endCn: Byte; function GetIndex(x, y: Longint): Longint; begin GetIndex := (y - 1) * s + (x - 1); end; function GetX(index: Longint): Longint; begin GetX := (index mod s) + 1; end; function GetY(index: LongInt): Longint; begin GetY := (index div s) + 1; end; function GetUsed(index: Longint; cn: Byte): Boolean; begin GetUsed := (used[index] and (1 shl cn)) > 0; end; procedure SetUsed(index: Longint; cn: Byte); begin used[index] := used[index] or (1 shl cn); end; function GetFromDir(index: Longint; cn: Byte): Byte; begin GetFromDir := 1 + (fromDir[index] shr (2 * cn)) mod 4; end; procedure SetFromDir(index: Longint; cn: Byte; AFrom: Byte); begin fromDir[index] := fromDir[index] or ((AFrom-1) shl (2 * cn)); end; function GetFromDifCn(index: Longint; cn: Byte): Boolean; begin GetFromDifCn := (fromDifCn[index] and (1 shl cn)) > 0; end; procedure SetFromDifCn(index: Longint; cn: Byte); begin fromDifCn[index] := fromDifCn[index] or (1 shl cn); end; function MoveIndex(dx, dy: Shortint; index:Longint): Longint; var x, y:Longint; begin x := GetX(index) + dx; y := GetY(index) + dy; if x > s then x := x - s; if x < 1 then x := x + s; if y > r then y := y - r; if y < 1 then y := y + r; MoveIndex := GetIndex(x, y); end; procedure ReadInput(filename: String); var c:Char; i, j: Longint; index: Longint; f:Text; begin Assign(f, filename); Reset(f); Readln(f, r, s); for j := 1 to r do begin for i := 1 to s do begin index := GetIndex(i, j); read(f, c); case c of '*':begin startX := i; startY := j; plan[index] := sqEmpty; end; '$': plan[index] := sqExit; '.': plan[index] := sqEmpty; '#': plan[index] := sqBlock; 'c': plan[index] := sqKeyRed; 'z': plan[index] := sqKeyGreen; 'm': plan[index] := sqKeyBlue; 'f': plan[index] := sqKeyMagenta; 'C': plan[index] := sqDoorRed; 'Z': plan[index] := sqDoorGreen; 'M': plan[index] := sqDoorBlue; 'F': plan[index] := sqDoorMagenta; end; end; readln(f); end; Close(f); end; procedure WriteOutput(filename: String); var f:Text; start: LongInt; index: Longint; cn: Byte; dir: Byte; i: Longint; begin Assign(f, filename); Rewrite(f); if endIndex = -1 then begin writeln(f, 'NELZE'); end else begin index := endIndex; cn := endCn; start := GetIndex(startX, startY); qPos := 0; while (index <> start) or (cn > 0) do begin dir := GetFromDir(index, cn); inc(qPos); queueI[qPos] := dir; if GetFromDifCn(index, cn) then begin cn := (cn xor plan[index]); end; index := MoveIndex(-availMoves[dir, 1], -availMoves[dir, 2], index); end; for i:=qPos downto 1 do begin write(f, availMovesName[queueI[i]]); end; writeln(f); end; Close(f); end; var index: Longint; cn: Byte; i: Longint; newIndex: Longint; newCn: Byte; sqType: Byte; keysNeeded: Byte; begin ReadInput('pyramida.in'); endIndex := -1; qCount := 0; qPos := 0; index := GetIndex(startX, startY); queueI[0] := index; queueCn[0] := 0; SetUsed(index, 0); while qPos <= qCount do begin index := queueI[qPos]; cn := queueCn[qPos]; for i:=1 to availMovesCnt do begin newIndex := MoveIndex(availMoves[i, 1], availMoves[i, 2], index); newCn := cn; sqType := plan[newIndex]; keysNeeded := sqType and KeyMask; if sqType < sqExit then newCn := newCn or sqType; if not GetUsed(newIndex, newCn) then if (sqType < sqBlock) or ((sqType > sqBlock) and (keysNeeded and newCn = keysNeeded)) then begin SetUsed(newIndex, newCn); SetFromDir(newIndex, newCn, i); if newCn<>cn then SetFromDifCn(newIndex, newCn); qCount := qCount + 1; queueI[qCount] := newIndex; queueCn[qCount] := newCn; if sqType = sqExit then begin qCount := 0; { přerušení cyklu while - vyprázdníme frontu políček } endIndex := newIndex; endCn := newCn; Break; end; end; end; qPos := qPos + 1; end; WriteOutput('pyramida.out'); end.