Druga odpowiedź jest już dość długa, więc zostawiam ją bez zmian. Ta odpowiedź jest o wiele lepsza, prostsza, a także poprawna, podczas gdy druga ma pewne skrajne przypadki, które doprowadzą do błędnej odpowiedzi - pozostawię to ćwiczenie czytelnikowi.
Uwaga:Podziały wierszy są dodawane dla większej przejrzystości. Cały blok to jedno zapytanie
;with Walker(StartX,StartY,X,Y,Visited) as (
select X,Y,X,Y,CAST('('+right(X,3)+','+right(Y,3)+')' as Varchar(Max))
from puzzle
union all
select W.StartX,W.StartY,P.X,P.Y,W.Visited+'('+right(P.X,3)+','+right(P.Y,3)+')'
from Walker W
join Puzzle P on
(W.X=P.X and W.Y=P.Y+1 OR -- these four lines "collect" a cell next to
W.X=P.X and W.Y=P.Y-1 OR -- the current one in any direction
W.X=P.X+1 and W.Y=P.Y OR
W.X=P.X-1 and W.Y=P.Y)
AND W.Visited NOT LIKE '%('+right(P.X,3)+','+right(P.Y,3)+')%'
)
select X, Y, Visited
from
(
select W.X, W.Y, W.Visited, rn=row_number() over (
partition by W.X,W.Y
order by len(W.Visited) desc)
from Walker W
left join Walker Other
on Other.StartX=W.StartX and Other.StartY=W.StartY
and (Other.Y<W.Y or (Other.Y=W.Y and Other.X<W.X))
where Other.X is null
) Z
where rn=1
Pierwszym krokiem jest skonfigurowanie rekurencyjnego wyrażenia tabelowego „walker”, które rozpocznie się od każdej komórki i przemieści się tak daleko, jak to możliwe, bez cofania żadnego kroku. Upewnienie się, że komórki nie są ponownie odwiedzane, odbywa się za pomocą kolumny odwiedzonej, która przechowuje każdą odwiedzoną komórkę z każdego punktu początkowego. W szczególności ten warunek AND W.Visited NOT LIKE '%('+right(P.X,3)+','+right(P.Y,3)+')%'
odrzuca komórki, które już odwiedził.
Aby zrozumieć, jak działa reszta, musisz spojrzeć na wynik wygenerowany przez „Walker” CTE, uruchamiając „Select * from Walker order by StartX, StartY” po CTE. „Kawałek” z 5 komórkami pojawia się w co najmniej 5 grupach, każda z innym (StartX,StartY)
, ale każda grupa ma wszystkie 5 (X,Y)
elementy o różnych ścieżkach „Odwiedzonych”.
Podzapytanie (Z) używa LEFT JOIN + IS NULL, aby usunąć grupy do jednego wiersza w każdej grupie, który zawiera „pierwszą współrzędną XY”, określoną przez warunek
Other.StartX=W.StartX and Other.StartY=W.StartY
and (Other.Y<W.Y or (Other.Y=W.Y and Other.X<W.X))
Intencją jest, aby każda komórka, którą można odwiedzić, zaczynając od (StartX, StartY), porównać z inną komórką w tej samej grupie i znaleźć komórkę, w której ŻADNA INNA komórka nie znajduje się w wyższym wierszu lub jeśli znajdują się w ten sam wiersz, znajduje się na lewo od tej komórki. To jednak wciąż pozostawia nam zbyt wiele wyników. Rozważ tylko fragment z 2 komórkami w punktach (3,4) i (4,4):
StartX StartY X Y Visited
3 4 3 4 (3,4) ******
3 4 4 4 (3,4)(4,4)
4 4 4 4 (4,4)
4 4 3 4 (4,4)(3,4) ******
Pozostały 2 wiersze z "pierwszą współrzędną XY" (3,4), oznaczoną ******
. Potrzebujemy tylko jednego wiersza, więc używamy Row_Number, a ponieważ numerujemy, równie dobrze możemy wybrać najdłuższy Visited
ścieżki, która dałaby nam jak najwięcej komórki w kawałku, jak tylko możemy.
Ostatnie zapytanie zewnętrzne po prostu pobiera pierwsze wiersze (RN=1) z każdej podobnej grupy (X,Y).
Aby wyświetlić WSZYSTKIE komórki każdego elementu, zmień linięselect X, Y, Visited
w środku do
select X, Y, (
select distinct '('+right(StartX,3)+','+right(StartY,3)+')'
from Walker
where X=Z.X and Y=Z.Y
for xml path('')
) PieceCells
Które dają ten wynik
X Y PieceCells
1 1 (1,1)(2,1)(2,2)(3,2)
3 4 (3,4)(4,4)
5 6 (5,6)
7 5 (7,5)(8,5)(9,5)
8 1 (10,1)(8,1)(8,2)(9,1)(9,2)(9,3)