PL/SQL解决 数独 九宫图

[复制链接]
查看11 | 回复9 | 2008-1-2 17:35:53 | 显示全部楼层 |阅读模式
drop table temp;
create table temp(row_id char(1), col_id char(1),rol_id char(2), qtyvarchar2(9), back_qty varchar2(9), set_qtyvarchar2(9), rmkchar(1));
begin
delete from temp;
for i in 1..9 loop
for j in 1..9 loop

insert into temp values(i,j,null,null,null,null,'N');
end loop;
end loop;
update temp set qty = 9,rmk = 'Y'where row_id = 1 andcol_id = 1;
update temp set qty = 2,rmk = 'Y'where row_id = 1 andcol_id = 3;
update temp set qty = 8,rmk = 'Y'where row_id = 1 andcol_id = 9;
update temp set qty = 5,rmk = 'Y'where row_id = 2 andcol_id = 2;
update temp set qty = 3,rmk = 'Y'where row_id = 2 andcol_id = 8;
update temp set qty = 1,rmk = 'Y'where row_id = 2 andcol_id = 9;
update temp set qty = 9,rmk = 'Y'where row_id = 3 andcol_id = 5;
update temp set qty = 7,rmk = 'Y'where row_id = 3 andcol_id = 6;
update temp set qty = 1,rmk = 'Y'where row_id = 4 andcol_id = 1;
update temp set qty = 2,rmk = 'Y'where row_id = 4 andcol_id = 5;
update temp set qty = 4,rmk = 'Y'where row_id = 4 andcol_id = 8;
update temp set qty = 9,rmk = 'Y'where row_id = 5 andcol_id = 3;
update temp set qty = 3,rmk = 'Y'where row_id = 5 andcol_id = 4;
update temp set qty = 4,rmk = 'Y'where row_id = 5 andcol_id = 6;
update temp set qty = 7,rmk = 'Y'where row_id = 5 andcol_id = 7;
update temp set qty = 7,rmk = 'Y'where row_id = 6 andcol_id = 2;
update temp set qty = 1,rmk = 'Y'where row_id = 6 andcol_id = 5;
update temp set qty = 5,rmk = 'Y'where row_id = 6 andcol_id = 9;
update temp set qty = 8,rmk = 'Y'where row_id = 7 andcol_id = 4;
update temp set qty = 4,rmk = 'Y'where row_id = 7 andcol_id = 5;
update temp set qty = 6,rmk = 'Y'where row_id = 8 andcol_id = 1;
update temp set qty = 1,rmk = 'Y'where row_id = 8 andcol_id = 2;
update temp set qty = 8,rmk = 'Y'where row_id = 8 andcol_id = 8;
update temp set qty = 2,rmk = 'Y'where row_id = 9 andcol_id = 1;
update temp set qty = 1,rmk = 'Y'where row_id = 9 andcol_id = 7;
update temp set qty = 3,rmk = 'Y'where row_id = 9 andcol_id = 9;

update temp set rol_id = 1 where row_id3 and row_id6 and row_id3 and col_id6 and col_idv_qty;
if sql%found then

v_row := v_row + 1;
end if;
end loop;
for rec_1 in(select *from temp where rmk = 'N' ) loop
for i in 1 .. length(rec_1.qty) loop

v := substr(rec_1.qty,i,1);


select count(*) into v_count from temp where row_id = rec_1.row_id

and qty like '%'||v||'%';

if v_count = 1 then

update temp

set qty = v,

rmk = 'Y'

where row_id = rec_1.row_id and col_id = rec_1.col_id;

if sql%found then

v_row := v_row + 1;

end if;

exit;

end if;


select count(*) into v_count from temp where col_id= rec_1.col_id

and qty like '%'||v||'%';

if v_count = 1 then

update temp

set qty = v,

rmk = 'Y'

where row_id = rec_1.row_id and col_id = rec_1.col_id;

if sql%found then

v_row := v_row + 1;

end if;

exit;

end if;


select count(*) into v_count from temp where rol_id= rec_1.rol_id

and qty like '%'||v||'%';

if v_count = 1 then

update temp

set qty = v,

rmk = 'Y'

where row_id = rec_1.row_id and col_id = rec_1.col_id;

if sql%found then

v_row := v_row + 1;

end if;

exit;

end if;

end loop;
end loop;
return v_row;
end;
回复

使用道具 举报

千问 | 2008-1-2 17:35:53 | 显示全部楼层
速度怎么样,以前newkid也写过
回复

使用道具 举报

千问 | 2008-1-2 17:35:53 | 显示全部楼层
强!
回复

使用道具 举报

千问 | 2008-1-2 17:35:53 | 显示全部楼层
占个位,慢慢再看。
回复

使用道具 举报

千问 | 2008-1-2 17:35:53 | 显示全部楼层
我以前写的:
http://www.itpub.net/thread-1074409-1-1.html
自从11GR2推出递归WITH,用一句SQL解决也不是难事了,等我找一下贴出来。
回复

使用道具 举报

千问 | 2008-1-2 17:35:53 | 显示全部楼层
这是 Anton Scheffer 写的:
with x( s, ind ) as
( select sud, instr( sud, ' ' )
from ( select '5376195986 8 6 348 317 2 6 6284195879' sud from dual )
union all
select substr( s, 1, ind - 1 ) || z || substr( s, ind + 1 )
, instr( s, ' ', ind + 1 )
from x
, ( select to_char( rownum ) z
from dual
connect by rownum0
and not exists ( select null

from ( select rownum lp

from dual

connect by rownum <= 9

)

where z = substr( s, trunc( ( ind - 1 ) / 9 ) * 9 + lp, 1 )

orz = substr( s, mod( ind - 1, 9 ) - 8 + lp * 9, 1 )

orz = substr( s, mod( trunc( ( ind - 1 ) / 3 ), 3 ) * 3

+ trunc( ( ind - 1 ) / 27 ) * 27 + lp

+ trunc( ( lp - 1 ) / 3 ) * 6

, 1 )

)
)
select s
from x
where ind = 0
/
我自己写的版本,代码多一些但是效率较高:
VAR str VARCHAR2(81);
EXEC :str := '5376195986 8 6 348 317 2 6 6284195879';
WITH grid AS (
SELECT LEVEL
AS pos ------- 字符串位置1-18编号
,FLOOR((LEVEL-1)/9)
AS r ------- 哪一行, 编号0-8
,MOD(LEVEL-1,9)
AS c ------- 哪一列, 编号0-8
,FLOOR(FLOOR((LEVEL-1)/9)/3)*3 + FLOOR(MOD(LEVEL-1,9)/3) AS g ------- 哪一格, 编号0-8
FROM DUAL
CONNECT BY LEVEL<=81
)
,all_pos AS ( ------ 每个POS上的字符,如果是n=1-9中的任何一个,将会到哪个位置取占用标记, 这是所有位置(1-81)和所有n的笛卡尔积。递归时求出哪个n可以用在哪个位置
SELECT pos,n
,grid.r*9+n AS r --------- 所有行占用标记,按照n=1-9凑在一起:第0行的9个||第1行的9个||...||第8行的9个, 这里根据n和r算出在占用标记里的位置
,grid.c*9+n AS c
,grid.g*9+n AS g
FROM grid,(SELECT LEVEL n FROM DUAL CONNECT BY LEVEL<=9)
)
,g2 AS
--------- 所有行占用标记,按照n=1-9凑在一起:第0行的9个||第1行的9个||...||第8行的9个
(SELECT LPAD(SUM(n) OVER(PARTITION BY r),9,'0') rs
,LPAD(SUM(n) OVER(PARTITION BY c),9,'0') cs
,LPAD(SUM(n) OVER(PARTITION BY g),9,'0') gs
,r,c,g
FROM (SELECT CASE WHEN SUBSTR(:str,pos,1) BETWEEN '1' AND '9' THEN POWER(10,9-SUBSTR(:str,pos,1)) ELSE 0 END n

,r,c,g
FROM grid
)
)
,t(s,rs,cs,gs,next_pos,lvl) AS (
SELECT CAST(:str AS VARCHAR2(81)) ----- 拼出初始的占用标记串
,CAST((SELECT LISTAGG(rs) WITHIN GROUP(ORDER BY r) FROM (SELECT DISTINCT r,rs FROM g2)) AS VARCHAR2(81)) rs
,CAST((SELECT LISTAGG(cs) WITHIN GROUP(ORDER BY c) FROM (SELECT DISTINCT c,cs FROM g2)) AS VARCHAR2(81)) cs
,CAST((SELECT LISTAGG(gs) WITHIN GROUP(ORDER BY g) FROM (SELECT DISTINCT g,gs FROM g2)) AS VARCHAR2(81)) gs
,INSTR(:str,' ')
,1
FROM DUAL
UNION ALL
SELECT SUBSTR(t.s,1,t.next_pos-1)||a.n||SUBSTR(t.s,t.next_pos+1)
,SUBSTR(t.rs,1,a.r-1)||'1'||SUBSTR(t.rs,a.r+1)
,SUBSTR(t.cs,1,a.c-1)||'1'||SUBSTR(t.cs,a.c+1)
,SUBSTR(t.gs,1,a.g-1)||'1'||SUBSTR(t.gs,a.g+1)
,INSTR(t.s,' ',t.next_pos+1)
,lvl+1
FROM t
,all_pos a
WHERE t.next_pos = a.pos
AND SUBSTR(t.rs,a.r,1)='0'
AND SUBSTR(t.cs,a.c,1)='0'
AND SUBSTR(t.gs,a.g,1)='0'
)
SELECT t.s FROM t WHERE next_pos=0;
回复

使用道具 举报

千问 | 2008-1-2 17:35:53 | 显示全部楼层
只有唯一解的好求,有非唯一解的,才是挑战
回复

使用道具 举报

千问 | 2008-1-2 17:35:53 | 显示全部楼层
原帖由 lastwinner 于 2011-9-2 00:20 发表
只有唯一解的好求,有非唯一解的,才是挑战

我写的方法全都能适用多个解,但也得分情况了,如果就输入一个空白字符串,要找出所有解?
回复

使用道具 举报

千问 | 2008-1-2 17:35:53 | 显示全部楼层
抽空看了一下楼主的程序,其实没有写完,企图把select * from temp wherermk = 'N'走一遍就得出答案,还早着呢。
回复

使用道具 举报

千问 | 2008-1-2 17:35:53 | 显示全部楼层
Why don't you guys using the bit value operation instead of using string manipulation? That could be much faster and you can implement a real set-based PL/SQL algorithm.
Give you guys some hint:
each empty cell, the initial possible values could be 1+2+4+8+...+2^^8=511
Good luck!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题

0

回帖

4882万

积分

论坛元老

Rank: 8Rank: 8

积分
48824836
热门排行