楼主: newkid

[精华] ITPUB第2届“盛拓传媒杯”SQL数据库编程大赛第二期参考思路

[复制链接]
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
41#
发表于 2013-12-4 06:55 | 只看该作者
newkid 发表于 2013-12-4 06:41
25楼和31楼都有同样的错,在WHERE里面判断第二号邻居的条件有一个OR错写成为了AND, 现在改过来了。

31楼好了

ID             1     2     3     4     5     6     7     8     9    10    11    12    13    14    15   100
---------- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
146          .12   .03     0   .01     0   .01   .08   .14   .11   .13   .07   .28   .28   .17     0 21.45

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
42#
 楼主| 发表于 2013-12-4 09:19 | 只看该作者
改良后的递归法,推理过程简单多了,但最后的校验还是有点啰嗦。

WITH cells AS (                      ------- 构造所有单元格及其坐标
    SELECT LEVEL cell_id              -------- 从左上角开始,从左到右从上到下从1开始编号
          ,CEIL(LEVEL/:v_width) r     -------- 行号,1~v_height
          ,MOD(LEVEL-1,:v_width)+1 c  -------- 列号,1~v_width
          ,SUBSTR(:v_str,LEVEL,1) cnt -------- 该单元格周围的地雷数目
      FROM DUAL CONNECT BY LEVEL<=:v_width*:v_height
)
,cells2 AS (
SELECT cell_id,r,c,DECODE(cnt,' ',0,TO_NUMBER(cnt)) AS cnt
       ,CASE WHEN r>1 AND c>1         THEN cell_id-:v_width-1   ---- 左上方邻居的id
             WHEN r>1                 THEN cell_id-:v_width     ---- 正上方邻居的id
             WHEN c>1                 THEN cell_id-1            ---- 左边邻居的id
        END AS n_id   ----- 参照邻居的id
       ,CASE WHEN r>1                 THEN r-1 ELSE r END AS n_r ----参照邻居的行数
       ,CASE WHEN c>1                 THEN c-1 ELSE c END AS n_c ----参照邻居的列数
   from cells
)
,empty_cells AS (
SELECT  --------- 找出所有空位及其邻居位置, 空位是未标数字的单元格,可能有地雷也可能没有               
        cc.*
       ,ROWNUM empty_id ---------- 按顺序为空位取编号,递归的时候每层编号递增,按序猜测每个空位
        ------ 如果某个方位的邻居不存在,比如边界上,把该邻居的id设为大数999999, 这样SUBSTR不报错只是返回NULL
       ,CASE WHEN n_r>1 AND n_c>1         THEN n_id-:v_width-1  ELSE 999999 END AS n1  ----- 参照邻居的左上角
       ,CASE WHEN n_r>1                   THEN n_id-:v_width    ELSE 999999 END AS n2  ----- 参照邻居的正上方
       ,CASE WHEN n_r>1 AND n_c<:v_width  THEN n_id-:v_width+1  ELSE 999999 END AS n3  ----- 参照邻居的右上角
       ,CASE WHEN n_c>1                   THEN n_id-1           ELSE 999999 END AS n4  ----- 参照邻居的左侧
       ,CASE WHEN n_r<:v_height AND n_c>1 THEN n_id+:v_width-1  ELSE 999999 END AS n6  ----- 参照邻居的左下角
       ,NVL(TO_NUMBER(TRIM(SUBSTR(:v_str,n_id,1))),0)  n_cnt
   FROM (SELECT * FROM cells2
          WHERE cnt=0          ------- 空位
         ORDER BY CASE WHEN cell_id<=2*:v_width THEN c ELSE 9999 END  -------------- 霹雳火思路
                           ,CASE WHEN cell_id<=2*:v_width THEN r ELSE 9999 END
                           ,cell_id
        ) cc
)
,guess AS (SELECT 0 AS mine FROM DUAL UNION ALL SELECT 1 FROM DUAL)
,t(m_cnt     ---- 到目前为止猜测为地雷的总数, 达到:V_CNT时就结束
   ,empty_id  ---- 目前要猜测哪个空位
   ,res       ---- 最后输出的结果。每猜出一个雷就拼一个 * 进去
   ) AS (
SELECT 0   ------- 未猜测之前总雷数为0   
       ,1   ------- 从第一个空位开始遍历
       ,CAST(:v_str AS VARCHAR2(1000))  -------- 输出初始为原分布,在递归过程中嵌入地雷
   FROM DUAL
UNION ALL
SELECT m_cnt + guess.mine ---- 总雷数递增或不变,取决于当前猜测
       ,t.empty_id+1  ------- 空单元格编号推进
       ,SUBSTR(res,1,e.cell_id-1)||CASE WHEN guess.mine=1 THEN '*' ELSE ' ' END||SUBSTR(t.res,e.cell_id+1) ---在输出结果中嵌入地雷
   FROM t
       ,empty_cells e
       ,guess  ---和两种猜测做笛卡尔积:0:没有地雷,1:有地雷
WHERE t.empty_id<=(SELECT MAX(empty_id) FROM empty_cells)   ------- 空位编号推进直至最大
        AND t.empty_id=e.empty_id   ------连接到empty_cells表,取当前猜测空位的坐标、邻居等信息
        AND  t.m_cnt<:v_cnt  ---- 总雷数不能溢出
        AND (e.cell_id=1 OR
             CASE WHEN e.n_cnt=0 THEN
                       DECODE(SUBSTR(t.res,e.n_id,1),'*',1,0)
                  WHEN DECODE(SUBSTR(t.res,e.n1,1),'*',1,0)
                      +DECODE(SUBSTR(t.res,e.n2,1),'*',1,0)                  
                      +DECODE(SUBSTR(t.res,e.n3,1),'*',1,0)                  
                      +DECODE(SUBSTR(t.res,e.n4,1),'*',1,0)                  
                      +DECODE(SUBSTR(t.res,e.n6,1),'*',1,0)
                      <e.n_cnt THEN 1
                  ELSE 0
             END = guess.mine                  
             )
)
,t2 AS (
SELECT ROWNUM id,res, TRANSLATE(res,'12345678','        ') as S
FROM t
WHERE m_cnt=:v_cnt ---------- 总雷数必须符合
)
SELECT res FROM (
SELECT res
FROM ( SELECT t2.id
              ,cells.cell_id
              ,t2.res
              ,CASE WHEN SUBSTR(t2.res,cell_id,1) = '*' THEN ' '
                    ELSE TO_CHAR(CASE WHEN r>1 THEN ---- 不是第一行
                                           ----也不是第一列,那么可以取左上邻居
                                           CASE WHEN c>1 THEN DECODE(SUBSTR(s,cell_id-:v_width-1,1),'*',1,0) ELSE 0 END
                                           ----再取正上邻居
                                           +DECODE(SUBSTR(s,cell_id-:v_width,1),'*',1,0)
                                           ----如果非最右一列,还可以取右上邻居
                                           +CASE WHEN c<:v_width THEN DECODE(SUBSTR(s,cell_id-:v_width+1,1),'*',1,0) ELSE 0 END
                                      ELSE 0 ---- 如果是第一行,那么上述三个邻居都不存在
                                 END
                                +CASE WHEN c>1 THEN DECODE(SUBSTR(s,cell_id-1,1),'*',1,0) ELSE 0 END --- 如果非第一列则取左方邻居
                                +CASE WHEN c<:v_width THEN DECODE(SUBSTR(s,cell_id+1,1),'*',1,0) ELSE 0 END --- 如果非最后一列则取右方邻居
                                +CASE WHEN r<:v_height THEN ---- 如果不是最后一行
                                            ----也不是第一列,那么可以取左下邻居
                                            CASE WHEN c>1 THEN DECODE(SUBSTR(s,cell_id+:v_width-1,1),'*',1,0) ELSE 0 END
                                            ----再取正下邻居
                                           +DECODE(SUBSTR(s,cell_id+:v_width,1),'*',1,0)
                                            ----如果非最右一列,还可以取右下邻居
                                           +CASE WHEN c<:v_width THEN DECODE(SUBSTR(s,cell_id+:v_width+1,1),'*',1,0) ELSE 0 END
                                      ELSE 0 ---- 如果是最后一行,那么上述三个邻居都不存在
                                 END
                                 )
               END str
           FROM t2, cells
        )
GROUP BY id,res
HAVING REPLACE(LISTAGG(str) WITHIN GROUP (ORDER BY cell_id) ,'0',' ')=:v_str
)
;

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
43#
 楼主| 发表于 2013-12-4 10:56 | 只看该作者
加强递归过程中的空位互相校验,比起事后校验的代码少一些:

WITH cells AS (                      ------- 构造所有单元格及其坐标
    SELECT LEVEL cell_id              -------- 从左上角开始,从左到右从上到下从1开始编号
          ,CEIL(LEVEL/:v_width) r     -------- 行号,1~v_height
          ,MOD(LEVEL-1,:v_width)+1 c  -------- 列号,1~v_width
          ,SUBSTR(:v_str,LEVEL,1) cnt -------- 该单元格周围的地雷数目
      FROM DUAL CONNECT BY LEVEL<=:v_width*:v_height
)
,cells2 AS (
SELECT cell_id,r,c,DECODE(cnt,' ',0,TO_NUMBER(cnt)) AS cnt
       ,CASE WHEN r>1 AND c>1         THEN cell_id-:v_width-1   ---- 左上方邻居的id
             WHEN r>1                 THEN cell_id-:v_width     ---- 正上方邻居的id
             WHEN c>1                 THEN cell_id-1            ---- 左边邻居的id
        END AS n_id   ----- 参照邻居的id
       ,CASE WHEN r>1                 THEN r-1 ELSE r END AS n_r ----参照邻居的行数
       ,CASE WHEN c>1                 THEN c-1 ELSE c END AS n_c ----参照邻居的列数
   from cells
)
,empty_cells AS (
SELECT  --------- 找出所有空位及其邻居位置, 空位是未标数字的单元格,可能有地雷也可能没有               
        cc.*
       ,ROWNUM empty_id ---------- 按顺序为空位取编号,递归的时候每层编号递增,按序猜测每个空位
        ------ 如果某个方位的邻居不存在,比如边界上,把该邻居的id设为大数999999, 这样SUBSTR不报错只是返回NULL
       ,CASE WHEN n_r>1 AND n_c>1         THEN n_id-:v_width-1  ELSE 999999 END AS n1  ----- 参照邻居的左上角
       ,CASE WHEN n_r>1                   THEN n_id-:v_width    ELSE 999999 END AS n2  ----- 参照邻居的正上方
       ,CASE WHEN n_r>1 AND n_c<:v_width  THEN n_id-:v_width+1  ELSE 999999 END AS n3  ----- 参照邻居的右上角
       ,CASE WHEN n_c>1                   THEN n_id-1           ELSE 999999 END AS n4  ----- 参照邻居的左侧
       ,CASE WHEN n_r<:v_height AND n_c>1 THEN n_id+:v_width-1  ELSE 999999 END AS n6  ----- 参照邻居的左下角
       ,NVL(TO_NUMBER(TRIM(SUBSTR(:v_str,n_id,1))),0)  n_cnt
       ,CASE WHEN r>1 AND c>1 AND SUBSTR(:v_str,cell_id-:v_width-1,1)=' '  THEN cell_id-:v_width-1 ELSE 0 END AS en1  ---- 左上方邻居的id
       ,CASE WHEN r>1 AND SUBSTR(:v_str,cell_id-:v_width,1)=' '            THEN cell_id-:v_width   ELSE 0 END AS en2  ---- 正上方邻居的id
       ,CASE WHEN c>1 AND SUBSTR(:v_str,cell_id-1,1)=' '                   THEN cell_id-1          ELSE 0 END AS en4  ---- 左边邻居的id
   FROM (SELECT * FROM cells2
          WHERE cnt=0          ------- 空位
         ORDER BY CASE WHEN cell_id<=2*:v_width THEN c ELSE 9999 END  -------------- 霹雳火思路
                           ,CASE WHEN cell_id<=2*:v_width THEN r ELSE 9999 END
                           ,cell_id
        ) cc
)
,guess AS (SELECT 0 AS mine FROM DUAL UNION ALL SELECT 1 FROM DUAL)
,t(m_cnt     ---- 到目前为止猜测为地雷的总数, 达到:V_CNT时就结束
   ,empty_id  ---- 目前要猜测哪个空位
   ,res       ---- 最后输出的结果。每猜出一个雷就拼一个 * 进去
   ) AS (
SELECT 0   ------- 未猜测之前总雷数为0   
       ,1   ------- 从第一个空位开始遍历
       ,CAST(:v_str AS VARCHAR2(1000))  -------- 输出初始为原分布,在递归过程中嵌入地雷
   FROM DUAL
UNION ALL
SELECT m_cnt + guess.mine ---- 总雷数递增或不变,取决于当前猜测
       ,t.empty_id+1  ------- 空单元格编号推进
       ,SUBSTR(res,1,e.cell_id-1)||CASE WHEN guess.mine=1 THEN '*' ELSE ' ' END||SUBSTR(t.res,e.cell_id+1) ---在输出结果中嵌入地雷
   FROM t
       ,empty_cells e
       ,guess  ---和两种猜测做笛卡尔积:0:没有地雷,1:有地雷
WHERE t.empty_id<=(SELECT MAX(empty_id) FROM empty_cells)   ------- 空位编号推进直至最大
        AND t.empty_id=e.empty_id   ------连接到empty_cells表,取当前猜测空位的坐标、邻居等信息
        AND  t.m_cnt<:v_cnt  ---- 总雷数不能溢出
        AND (e.cell_id=1 OR
             CASE WHEN e.n_cnt=0 THEN
                       DECODE(SUBSTR(t.res,e.n_id,1),'*',1,0)
                  WHEN DECODE(SUBSTR(t.res,e.n1,1),'*',1,0)
                      +DECODE(SUBSTR(t.res,e.n2,1),'*',1,0)                  
                      +DECODE(SUBSTR(t.res,e.n3,1),'*',1,0)                  
                      +DECODE(SUBSTR(t.res,e.n4,1),'*',1,0)                  
                      +DECODE(SUBSTR(t.res,e.n6,1),'*',1,0)
                      <e.n_cnt THEN 1
                  ELSE 0
             END = guess.mine                  
             AND CASE WHEN e.en1>0 THEN DECODE(SUBSTR(t.res,e.en1,1),'*',1,0) ELSE guess.mine END = guess.mine
             AND CASE WHEN e.en2>0 THEN DECODE(SUBSTR(t.res,e.en2,1),'*',1,0) ELSE guess.mine END = guess.mine
             AND CASE WHEN e.en4>0 THEN DECODE(SUBSTR(t.res,e.en4,1),'*',1,0) ELSE guess.mine END = guess.mine
             )
)
SELECT res
  FROM t
WHERE m_cnt=:v_cnt ---------- 总雷数必须符合
;

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
44#
发表于 2013-12-4 12:48 | 只看该作者
42楼47 43楼48
怎么差距这么大,又是优化器bug?
48这么快,都不象递归了,但第2个用例居然出错,(31*31的100个用例都全对)
147          .31  1.14    .3   .14   .34   .21   .27   .28 61.44  1.17 16.64
148          .16         .18   .13   .24   .09   .19   .18   .32    .2   .19   .41   .51   .29 29.29

已选择34行。

SQL> select width,height,cnt,cast(outs as varchar(100)) from dlz where qn=2;

WIDTH HEIGHT   CNT CAST(OUTSASVARCHAR(100))
----- ------ ----- ----------------------------------------------------------------------------------------------------
[   10     10    10        1 2       12   111   11  1 1       111   11  111   1   1 111122  1111 11 1221 112221  1   1 1]

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
45#
 楼主| 发表于 2013-12-4 23:00 | 只看该作者
再增强校验条件:
WITH cells AS (                      ------- 构造所有单元格及其坐标
    SELECT LEVEL cell_id              -------- 从左上角开始,从左到右从上到下从1开始编号
          ,CEIL(LEVEL/:v_width) r     -------- 行号,1~v_height
          ,MOD(LEVEL-1,:v_width)+1 c  -------- 列号,1~v_width
          ,SUBSTR(:v_str,LEVEL,1) cnt -------- 该单元格周围的地雷数目
      FROM DUAL CONNECT BY LEVEL<=:v_width*:v_height
)
,cells2 AS (
SELECT cell_id,r,c,DECODE(cnt,' ',0,TO_NUMBER(cnt)) AS cnt
       ,CASE WHEN r>1 AND c>1         THEN cell_id-:v_width-1   ---- 左上方邻居的id
             WHEN r>1                 THEN cell_id-:v_width     ---- 正上方邻居的id
             WHEN c>1                 THEN cell_id-1            ---- 左边邻居的id
        END AS n_id   ----- 参照邻居的id
       ,CASE WHEN r>1                 THEN r-1 ELSE r END AS n_r ----参照邻居的行数
       ,CASE WHEN c>1                 THEN c-1 ELSE c END AS n_c ----参照邻居的列数
   from cells
)
,empty_cells AS (
SELECT  --------- 找出所有空位及其邻居位置, 空位是未标数字的单元格,可能有地雷也可能没有               
        cc.*
       ,ROWNUM empty_id ---------- 按顺序为空位取编号,递归的时候每层编号递增,按序猜测每个空位
        ------ 如果某个方位的邻居不存在,比如边界上,把该邻居的id设为大数999999, 这样SUBSTR不报错只是返回NULL
       ,CASE WHEN n_r>1 AND n_c>1         THEN n_id-:v_width-1  ELSE 999999 END AS n1  ----- 参照邻居的左上角
       ,CASE WHEN n_r>1                   THEN n_id-:v_width    ELSE 999999 END AS n2  ----- 参照邻居的正上方
       ,CASE WHEN n_r>1 AND n_c<:v_width  THEN n_id-:v_width+1  ELSE 999999 END AS n3  ----- 参照邻居的右上角
       ,CASE WHEN n_c>1                   THEN n_id-1           ELSE 999999 END AS n4  ----- 参照邻居的左侧
       ,CASE WHEN n_r<:v_height AND n_c>1 THEN n_id+:v_width-1  ELSE 999999 END AS n6  ----- 参照邻居的左下角
       ,NVL(TO_NUMBER(TRIM(SUBSTR(:v_str,n_id,1))),0)  n_cnt
       ,CASE WHEN r>1 AND c>1 AND SUBSTR(:v_str,cell_id-:v_width-1,1)=' '  THEN cell_id-:v_width-1 ELSE 0 END AS en1  ---- 左上方邻居的id
       ,CASE WHEN r>1 AND SUBSTR(:v_str,cell_id-:v_width,1)=' '            THEN cell_id-:v_width   ELSE 0 END AS en2  ---- 正上方邻居的id
       ,CASE WHEN r>2 AND c<:v_width AND SUBSTR(:v_str,cell_id-:v_width+1,1)=' ' THEN cell_id-:v_width+1 ELSE 0 END AS en3  ---- 右上角邻居的id,由于排序的原因,从第三行开始, 此邻居才先于本单元猜测
       ,CASE WHEN c>1 AND SUBSTR(:v_str,cell_id-1,1)=' '                   THEN cell_id-1          ELSE 0 END AS en4  ---- 左边邻居的id
       ,CASE WHEN r=1 AND c>1 AND SUBSTR(:v_str,cell_id+:v_width-1,1)=' '  THEN cell_id+:v_width-1 ELSE 0 END AS en6  ---- 左下角邻居的id,由于排序的原因,只有在第一行, 此邻居才先于本单元猜测
   FROM (SELECT * FROM cells2
          WHERE cnt=0          ------- 空位
         ORDER BY CASE WHEN cell_id<=2*:v_width THEN c ELSE 9999 END  -------------- 霹雳火思路
                           ,CASE WHEN cell_id<=2*:v_width THEN r ELSE 9999 END
                           ,cell_id
        ) cc
)
,guess AS (SELECT 0 AS mine FROM DUAL UNION ALL SELECT 1 FROM DUAL)
,t(m_cnt     ---- 到目前为止猜测为地雷的总数, 达到:V_CNT时就结束
   ,empty_id  ---- 目前要猜测哪个空位
   ,res       ---- 最后输出的结果。每猜出一个雷就拼一个 * 进去
   ) AS (
SELECT 0   ------- 未猜测之前总雷数为0   
       ,1   ------- 从第一个空位开始遍历
       ,CAST(:v_str AS VARCHAR2(1000))  -------- 输出初始为原分布,在递归过程中嵌入地雷
   FROM DUAL
UNION ALL
SELECT m_cnt + guess.mine ---- 总雷数递增或不变,取决于当前猜测
       ,t.empty_id+1  ------- 空单元格编号推进
       ,SUBSTR(res,1,e.cell_id-1)||CASE WHEN guess.mine=1 THEN '*' ELSE ' ' END||SUBSTR(t.res,e.cell_id+1) ---在输出结果中嵌入地雷
   FROM t
       ,empty_cells e
       ,guess  ---和两种猜测做笛卡尔积:0:没有地雷,1:有地雷
WHERE t.empty_id<=(SELECT MAX(empty_id) FROM empty_cells)   ------- 空位编号推进直至最大
        AND t.empty_id=e.empty_id   ------连接到empty_cells表,取当前猜测空位的坐标、邻居等信息
        AND  t.m_cnt<=:v_cnt  ---- 总雷数不能溢出
        AND (e.cell_id=1 OR
             CASE WHEN e.n_cnt=0 THEN
                       DECODE(SUBSTR(t.res,e.n_id,1),'*',1,0)
                  WHEN DECODE(SUBSTR(t.res,e.n1,1),'*',1,0)
                      +DECODE(SUBSTR(t.res,e.n2,1),'*',1,0)                  
                      +DECODE(SUBSTR(t.res,e.n3,1),'*',1,0)                  
                      +DECODE(SUBSTR(t.res,e.n4,1),'*',1,0)                  
                      +DECODE(SUBSTR(t.res,e.n6,1),'*',1,0)
                      <e.n_cnt THEN 1
                  ELSE 0
             END = guess.mine                  
             AND CASE WHEN e.en1>0 THEN DECODE(SUBSTR(t.res,e.en1,1),'*',1,0) ELSE guess.mine END = guess.mine
             AND CASE WHEN e.en2>0 THEN DECODE(SUBSTR(t.res,e.en2,1),'*',1,0) ELSE guess.mine END = guess.mine
             AND CASE WHEN e.en3>0 THEN DECODE(SUBSTR(t.res,e.en3,1),'*',1,0) ELSE guess.mine END = guess.mine
             AND CASE WHEN e.en4>0 THEN DECODE(SUBSTR(t.res,e.en4,1),'*',1,0) ELSE guess.mine END = guess.mine
             AND CASE WHEN e.en6>0 THEN DECODE(SUBSTR(t.res,e.en6,1),'*',1,0) ELSE guess.mine END = guess.mine
             )
)
SELECT res
  FROM t
WHERE m_cnt=:v_cnt ---------- 总雷数必须符合
       AND t.empty_id=(SELECT MAX(empty_id) FROM empty_cells)+1
;

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
46#
发表于 2013-12-5 06:11 | 只看该作者
newkid 发表于 2013-12-4 23:00
再增强校验条件:
WITH cells AS (                      ------- 构造所有单元格及其坐标
    SELECT LEV ...

这下对了
ID             1     2     3     4     5     6     7     8     9    10    11    12    13    14   100
---------- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
148          .22   .14   .12    .1   .15   .18   .22   .09   .29   .15    .2   .55   .47   .35 29.97

使用道具 举报

回复
论坛徽章:
11
开发板块每日发贴之星
日期:2007-09-24 01:04:44开发板块每日发贴之星
日期:2010-07-29 01:01:01ERP板块每日发贴之星
日期:2010-07-29 01:01:01设计板块每日发贴之星
日期:2010-06-12 01:01:06ERP板块每日发贴之星
日期:2010-05-18 01:01:01数据库板块每日发贴之星
日期:2010-05-18 01:01:01设计板块每日发贴之星
日期:2008-10-16 01:03:26行业板块每日发贴之星
日期:2007-10-29 01:05:31ITPUB新首页上线纪念徽章
日期:2007-10-20 08:38:44开发板块每日发贴之星
日期:2007-10-18 01:05:07
47#
发表于 2013-12-8 09:40 | 只看该作者
观摩中

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
48#
发表于 2025-3-26 14:10 | 只看该作者
在oracle 19c中,34楼和42楼都报错了
SQL> @wdl5.txt
SELECT guess.mine AS solution_id
       *
ERROR at line 10:
ORA-01722: invalid number


SQL> @wdl6.txt
             WHEN c>1                      THEN cell_id-1                ---- 左边邻居的id
                                                                                     *
ERROR at line 12:
ORA-01722: invalid number

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
49#
 楼主| 发表于 2025-3-28 08:08 来自手机 | 只看该作者
当年有多热闹,现在就有多凄凉

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
50#
发表于 2025-3-29 20:25 | 只看该作者
让deepseek把43楼SQL改成的python,一次成功

  1. import itertools
  2. from typing import List, Dict, Any

  3. def sql_to_python(width: int, height: int, grid_str: str):
  4.     # CTE 1: cells
  5.     cells = [
  6.         {
  7.             'cell_id': i,
  8.             'r': (i - 1) // width + 1,
  9.             'c': (i - 1) % width + 1,
  10.             'cnt': grid_str[i-1] if i <= len(grid_str) else ' '
  11.         }
  12.         for i in range(1, width * height + 1)
  13.     ]
  14.    
  15.     # CTE 2: cells2
  16.     cells2 = []
  17.     for cell in cells:
  18.         cnt = cell['cnt']
  19.         r = cell['r']
  20.         c = cell['c']
  21.         cell_id = cell['cell_id']
  22.         
  23.         # 处理cnt值
  24.         if cnt == ' ':
  25.             cnt_val = 0
  26.         elif cnt == '*':
  27.             cnt_val = None
  28.         else:
  29.             cnt_val = int(cnt) if cnt.strip() else None
  30.         
  31.         # 计算n_id
  32.         if r > 1 and c > 1:
  33.             n_id = cell_id - width - 1
  34.         elif r > 1:
  35.             n_id = cell_id - width
  36.         elif c > 1:
  37.             n_id = cell_id - 1
  38.         else:
  39.             n_id = None
  40.         
  41.         cells2.append({
  42.             'cell_id': cell_id,
  43.             'r': r,
  44.             'c': c,
  45.             'cnt': cnt_val,
  46.             'n_id': n_id,
  47.             'n_r': r - 1 if r > 1 else r,
  48.             'n_c': c - 1 if c > 1 else c
  49.         })
  50.    
  51.     # CTE 3: empty_cells
  52.     empty_cells = []
  53.     # 先筛选出空单元格(cnt=0)
  54.     empty_candidates = [cell for cell in cells2 if cell['cnt'] == 0]
  55.    
  56.     # 按SQL中的ORDER BY排序
  57.     empty_candidates.sort(key=lambda x: (
  58.         9999 if x['cell_id'] > 2 * width else x['c'],
  59.         9999 if x['cell_id'] > 2 * width else x['r'],
  60.         x['cell_id']
  61.     ))
  62.    
  63.     for idx, cell in enumerate(empty_candidates, 1):
  64.         r = cell['r']
  65.         c = cell['c']
  66.         cell_id = cell['cell_id']
  67.         n_id = cell['n_id']
  68.         n_r = cell['n_r']
  69.         n_c = cell['n_c']
  70.         
  71.         # 计算邻居ID
  72.         n1 = n_id - width - 1 if (n_r > 1 and n_c > 1) else 999999
  73.         n2 = n_id - width if n_r > 1 else 999999
  74.         n3 = n_id - width + 1 if (n_r > 1 and n_c < width) else 999999
  75.         n4 = n_id - 1 if n_c > 1 else 999999
  76.         n6 = n_id + width - 1 if (n_r < height and n_c > 1) else 999999
  77.         
  78.         # 计算n_cnt
  79.         if n_id is None or n_id < 1 or n_id > width * height:
  80.             n_cnt = 0
  81.         else:
  82.             neighbor_char = grid_str[n_id-1]
  83.             if neighbor_char == '*' or not neighbor_char.strip():
  84.                 n_cnt = 0
  85.             else:
  86.                 n_cnt = int(neighbor_char)
  87.         
  88.         # 计算en1, en2, en4
  89.         en1 = cell_id - width - 1 if (r > 1 and c > 1 and grid_str[cell_id - width - 2] == ' ') else 0
  90.         en2 = cell_id - width if (r > 1 and grid_str[cell_id - width - 1] == ' ') else 0
  91.         en4 = cell_id - 1 if (c > 1 and grid_str[cell_id - 2] == ' ') else 0
  92.         
  93.         empty_cells.append({
  94.             **cell,
  95.             'empty_id': idx,
  96.             'n1': n1,
  97.             'n2': n2,
  98.             'n3': n3,
  99.             'n4': n4,
  100.             'n6': n6,
  101.             'n_cnt': n_cnt,
  102.             'en1': en1,
  103.             'en2': en2,
  104.             'en4': en4
  105.         })
  106.    
  107.     # CTE 4: guess
  108.     guess = [{'mine': 0}, {'mine': 1}]
  109.    
  110.     return {
  111.         'cells': cells,
  112.         'cells2': cells2,
  113.         'empty_cells': empty_cells,
  114.         'guess': guess
  115.     }

  116. def solve_minesweeper(width: int, height: int, grid_str: str, total_mines: int):
  117.     # 使用之前定义的函数生成基础数据
  118.     data = sql_to_python(width, height, grid_str)
  119.     empty_cells = data['empty_cells']
  120.     guess = data['guess']
  121.    
  122.     # 递归基 case
  123.     recursive_t = [{
  124.         'm_cnt': 0,
  125.         'empty_id': 1,
  126.         'res': grid_str
  127.     }]
  128.    
  129.     max_empty_id = max([ec['empty_id'] for ec in empty_cells]) if empty_cells else 0
  130.    
  131.     # 使用循环模拟递归(避免Python递归深度限制)
  132.     while True:
  133.         new_rows = []
  134.         for t in recursive_t:
  135.             current_empty_id = t['empty_id']
  136.             if current_empty_id > max_empty_id:
  137.                 continue
  138.             
  139.             # 找到当前empty_id对应的empty_cell
  140.             current_ec = next((ec for ec in empty_cells if ec['empty_id'] == current_empty_id), None)
  141.             if not current_ec:
  142.                 continue
  143.             
  144.             # 与guess做笛卡尔积
  145.             for g in guess:
  146.                 mine = g['mine']
  147.                
  148.                 # 检查WHERE条件
  149.                 if not (t['m_cnt'] + mine <= total_mines):
  150.                     continue
  151.                
  152.                 # 第一个条件:e.cell_id = 1
  153.                 if current_ec['cell_id'] == 1:
  154.                     pass  # 满足条件
  155.                 else:
  156.                     # 复杂条件检查
  157.                     n_cnt = current_ec['n_cnt']
  158.                     res = t['res']
  159.                     
  160.                     # 计算CASE WHEN e.n_cnt = 0...部分
  161.                     if n_cnt == 0:
  162.                         case_val = 1 if (current_ec['n_id'] <= len(res) and res[current_ec['n_id'] - 1] == '*') else 0
  163.                     else:
  164.                         # 计算各个邻居的雷数
  165.                         mine_count = 0
  166.                         for n in ['n1', 'n2', 'n3', 'n4', 'n6']:
  167.                             n_pos = current_ec[n]
  168.                             if n_pos <= len(res) and res[n_pos - 1] == '*':
  169.                                 mine_count += 1
  170.                         
  171.                         case_val = 1 if mine_count < n_cnt else 0
  172.                     
  173.                     # 检查主条件
  174.                     if case_val != mine:
  175.                         continue
  176.                     
  177.                     # 检查en1, en2, en4条件
  178.                     valid = True
  179.                     for en in ['en1', 'en2', 'en4']:
  180.                         en_pos = current_ec[en]
  181.                         if en_pos > 0:
  182.                             if en_pos > len(res):
  183.                                 valid = False
  184.                                 break
  185.                             en_val = 1 if res[en_pos - 1] == '*' else 0
  186.                             if en_val != mine:
  187.                                 valid = False
  188.                                 break
  189.                     if not valid:
  190.                         continue
  191.                
  192.                 # 构建新行
  193.                 new_res = (
  194.                     t['res'][:current_ec['cell_id'] - 1] +
  195.                     ('*' if mine == 1 else ' ') +
  196.                     t['res'][current_ec['cell_id']:]
  197.                 )
  198.                
  199.                 new_rows.append({
  200.                     'm_cnt': t['m_cnt'] + mine,
  201.                     'empty_id': current_empty_id + 1,
  202.                     'res': new_res
  203.                 })
  204.         
  205.         if not new_rows:
  206.             break
  207.         
  208.         # 添加新行到递归表(替换原有内容以模拟递归)
  209.         recursive_t = new_rows
  210.    
  211.     # 筛选最终结果
  212.     solutions = [t['res'] for t in recursive_t if t['m_cnt'] == total_mines]
  213.     return solutions

  214. # 使用示例
  215. if __name__ == "__main__":
  216.     width, height,total_mines,grid_str =9,9,27,' 2**2  2* 3**3  3* 3**3  3* 3**3  3* 3**3  3* 3**3  3* 3**3  3* 3**3  3* 2**2  2*'.replace('*', ' ')

  217.     solutions = solve_minesweeper(width, height, grid_str, total_mines)
  218.     print(f"找到 {len(solutions)} 个解:")
  219.     for i, sol in enumerate(solutions, 1):
  220.         print(f"解 {i}: {sol}")
复制代码

使用道具 举报

回复

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

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表