|
|
SQL> with
2 function f_check (p_nlist in varchar2) --这个函数主要是对主查询的每一个结果检查删除3行删除3列后是否还要元素存在,有就是一个解
3 return int
4 is
5 type r_rec is record( n int, --主查询是一个对1-36的整数串,x,y坐标也存储起来,方便后面删除判断
6 x int,
7 y int);
8
9 type t_rec is table of r_rec index by pls_integer;
10 l_rec t_rec;
11 l_tmp t_rec;
12
13 l_cnt int := 0;
14 l_t_cnt int := -1;
15
16 begin
17
18 select n, --将主查询的每一个结果转换成一个10个元素的集合
19 ceil(n/6),
20 mod(n-1,6)+1
21 bulk collect into l_rec
22 from (
23 select regexp_substr(p_nlist,'[^,]+',1,level) n
24 from dual
25 connect by level <= regexp_count(p_nlist,',')+1
26 );
27
28 for rec in ( with t as (
29 select substr(sys_connect_by_path(n,','),2) rc
30 from (select level n from dual connect by level<=6)
31 where level = 3
32 connect by prior n < n
33 )
34 select a.rc x_rc,b.rc y_rc --这个隐式游标就是删除3个行和3个列的行列的全部组合 20*20=400个
35 from t a,t b ) loop
36
37 l_tmp := l_rec; --因为对主查询的每一个结果的判断,是用集合的方式删除元素,所以要先赋给一个临时对象
38
39 for i in l_tmp.first..l_tmp.last loop --先删行
40 if l_tmp.exists(i)=TRUE and instr(rec.x_rc,l_tmp(i).x) > 0 then --元素存在,并且元素的x坐标在被删除的3个行号集合中,则删除
41 l_tmp.delete(i);
42 end if;
43 end loop;
44
45 for i in l_tmp.first..l_tmp.last loop --同上,删除列
46 if l_tmp.exists(i)=TRUE and instr(rec.y_rc,l_tmp(i).y) > 0 then
47 l_tmp.delete(i);
48 end if;
49 end loop;
50
51 if l_tmp.count > 0 then --删完后,如果还剩余元素,就加1,删除3个行和3个列的行列的全部组合 20*20=400个,如果 l_cnt最后等于400
52 l_cnt := l_cnt + 1; --就表明,不管你怎么删除,都至少有一个元素存在,那就是结果了
53 else
54 return 0; --400组中的任何一组的判断,删了之后,没有任何元素剩余,那肯定就不是一个解
55 end if;
56
57 end loop;
58
59 select count(*)
60 into l_t_cnt --删除3个行和3个列的行列的全部组合 20*20=400个,其实就是400,用常量代替更快
61 from (
62 with t as (
63 select substr(sys_connect_by_path(n,','),2) rc
64 from (select level n from dual connect by level<=6)
65 where level = 3
66 connect by prior n < n
67 )
68 select a.rc x_rc,b.rc y_rc
69 from t a,t b
70 );
71
72 if l_cnt=l_t_cnt then --等于400 就返回1,是一个解
73 return 1;
74 else
75 return 0;
76 end if;
77
78 end;
79 t(n,c,x,y) as (select level,chr(64+level),ceil(level/6),mod(level-1,6)+1 from dual connect by level<=6*6),
80 s(lvl,n,nlist,clist) as (select 1, n,cast(n as varchar2(100)),c
81 from t
82 where t.n between 1 and 6
83 union all
84 select lvl + 1,
85 b.n,
86 nlist||','||b.n,
87 clist||','||b.c
88 from s,t b
89 where s.n < b.n
90 and (select greatest(sum(case when a.x = b.x then 1 end), --跟第六题的差不多,不同的是,这里只用判断行和列
91 sum(case when a.y = b.y then 1 end)
92 )
93 from t a
94 where instr(clist||','||b.c,a.c)>0
95 ) <= 2 --每行每列都不多与2
96 and lvl < 10
97 and b.n between (ceil((lvl+1)/2)-1)*6 + 1 and (ceil((lvl+1)/2)-1)*6 + 6*2 --由于有的行只有一个元素,那他的范围要多一行
98 )
99 select nlist from s where lvl = 10 and f_check(nlist)=1 and rownum <= 100 --速度比较慢,就只取前100个解吧。。。
100 /
|
|