|
坐标说明:
左下角:(0,0)
右上角: (12,2)
A点:(3,1)
F点:(12,2)
线段表示法:线的编号:小坐标:大坐标
垂直线编号: X0, X1, X2, ... X12
水平线编号:Y0,Y1,Y2
例子:
X3:1:2 表示(3,1)到(3,2)
路径是由上述线段拼起来的,用逗号隔开,相邻的要垂直。
with d as ( ------ 构造所有点
select x,y
from (select level-1 x from dual connect by level<=13)
,(select level-1 y from dual connect by level<=3)
)
,T(last_x,last_y,last_direction,path,len) as (
select 3,1,'*',cast('' as varchar2(400)),0 from dual
union all
select d.x,d.y
,case when t.last_x=d.x then 'x' else 'y' end
,t.path||','||j.line||':'||j.c1||':'||j.c2
,t.len+power(2,abs(t.last_x-d.x+t.last_y-d.y))
from t join d
on ((t.last_x=d.x and t.last_y<>d.y and t.last_direction<>'x') ---- 下个箭头方向要垂直
or
(t.last_y=d.y and t.last_x<>d.x and t.last_direction<>'y')
)
CROSS JOIN LATERAL --------- 这个LATERAL的目的是为了简化表达式,后面直接引用列名 line:c1:c2 拼到路径里面
(SELECT case when t.last_x=d.x then 'x'||d.x else 'y'||d.y end as line
,case when t.last_x=d.x then least(t.last_y,d.y) else least(t.last_x,d.x) end as c1
,case when t.last_x=d.x then greatest(t.last_y,d.y) else greatest(t.last_x,d.x) end as c2
FROM dual
) j
LEFT OUTER JOIN LATERAL --------- 这个LATERAL的目的是为了打散路径,以检查新加入的路径是否和旧路径有重叠
(select regexp_substr(s,'[^:]+',1,1) as line
,regexp_substr(s,'[^:]+',1,2) as c1
,regexp_substr(s,'[^:]+',1,3) as c2
from (
SELECT regexp_substr(t.path,'[^,]+',1,level) s
FROM dual
connect by level<=regexp_count(t.path,',')+1
)
) p
on j.line=p.line ---- 外连接条件:同一条线且坐标有交叉
and (p.c1>j.c1 and p.c1<j.c2 or p.c2>j.c1 and p.c2<j.c2
or j.c1>p.c1 and j.c1<p.c2 or j.c2>p.c1 and j.c2<p.c2
) ----- 四种交叠情形
where (last_x<>12 or last_y<>2)
and bitand(t.len,power(2,abs(t.last_x-d.x+t.last_y-d.y)))=0 ---- 要求所有长度都不一样
and p.line is null ---- 外连接为空,则不存在交叠
)
select count(*) from t where last_x=12 and last_y=2; |
|