Другият отговор вече е доста дълъг, така че го оставям такъв, какъвто е. Този отговор е много по-добър, по-прост и също така правилен, докато другият има някои крайни случаи, които ще доведат до грешен отговор - ще оставя това упражнение на читателя.
Забележка:За по-голяма яснота са добавени прекъсвания на редовете. Целият блок е една заявка
;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
Първата стъпка е да настроите израз на рекурсивна таблица "ходещ", който ще започне от всяка клетка и ще пътува доколкото може, без да проследява никоя стъпка. Уверяването, че клетките не са преразгледани, се извършва с помощта на посетената колона, която съхранява всяка клетка, която е посетена от всяка начална точка. По-специално, това условие AND W.Visited NOT LIKE '%('+right(P.X,3)+','+right(P.Y,3)+')%'
отхвърля клетки, които вече е посетил.
За да разберете как работи останалото, трябва да погледнете резултата, генериран от CTE на "Walker", като изпълните "Select * from Walker order by StartX, StartY" след CTE. „Парче“ с 5 клетки се появява в поне 5 групи, всяка с различен (StartX,StartY)
, но всяка група има всичките 5 (X,Y)
части с различни „Посетени“ пътища.
Подзаявката (Z) използва LEFT JOIN + IS NULL, за да премахне групите до един ред във всяка група, който съдържа „първата XY координата“, дефинирана от условието
Other.StartX=W.StartX and Other.StartY=W.StartY
and (Other.Y<W.Y or (Other.Y=W.Y and Other.X<W.X))
Намерението е всяка клетка, която може да бъде посетена, като се започне от (StartX, StartY), да се сравни една с друга клетка в същата група и да се намери клетката, в която НЯМА ДРУГА клетка на по-горен ред или ако са на същия ред, е отляво на тази клетка. Това обаче все още ни оставя с твърде много резултати. Помислете само за част от 2 клетки на (3,4) и (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) ******
Остават 2 реда с "първата XY координата" на (3,4), маркирани с ******
. Нуждаем се само от един ред, така че използваме Row_Number и тъй като номерираме, може да се спрем на най-дълго посетения Visited
път, което ще ни даде толкова много от клетките в парчето, както можем да получим.
Последната външна заявка просто взема първите редове (RN=1) от всяка подобна (X,Y) група.
За да покажете ВСИЧКИ клетки на всяко парче, променете редаselect X, Y, Visited
в средата до
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
Които дават този резултат
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)