|
这本来是个挺无聊的题目,碰上我这个无聊的人就来做一做。用的是经典的比特掩码法。
递归WITH方法:
VAR N NUMBER;
EXEC :N:=6;
WITH k AS (
SELECT POWER(2,LEVEL-1) id ------- 二进制的一位
,TRUNC((LEVEL-1)/:N) X
,MOD(LEVEL-1,:N) Y
FROM DUAL
CONNECT BY LEVEL<=:N*:N
)
,king AS (
SELECT k1.id,SUM(k2.id) AS m ----- m表示攻击范围
FROM k k1,k k2
WHERE k1.id<>k2.id
AND ABS(k1.x-k2.x)<=1 AND ABS(k1.y-k2.y)<=1
GROUP BY k1.id
)
,t(cnt,id,all_id,m) AS (
SELECT 1 cnt,id,id,m
FROM king
UNION ALL
SELECT t.cnt+1
,king.id
,t.all_id+king.id
,t.m + king.m - BITAND(t.m, king.m) --- BITOR
FROM t,king
WHERE t.cnt<:N
AND king.id>t.id
AND BITAND(t.m, king.id)=0
)
SELECT COUNT(*) FROM t WHERE cnt = :N;
COUNT(*)
----------
62266
Elapsed: 00:00:02.89
笛卡尔积,速度快得多:
WITH k AS (
SELECT POWER(2,LEVEL-1) id ------- 二进制的一位
,TRUNC((LEVEL-1)/6) X
,MOD(LEVEL-1,6) Y
FROM DUAL
CONNECT BY LEVEL<=6*6
)
,king AS (
SELECT k1.id,SUM(k2.id) AS m
FROM k k1,k k2
WHERE k1.id<>k2.id
AND ABS(k1.x-k2.x)<=1 AND ABS(k1.y-k2.y)<=1
GROUP BY k1.id
)
SELECT COUNT(*)
FROM king k1,king k2,king k3,king k4,king k5,king k6
WHERE k1.id<k2.id AND k2.id<k3.id AND k3.id<k4.id AND k4.id<k5.id AND k5.id<k6.id
AND BITAND(k1.m, k2.id)=0
AND BITAND(k1.m, k3.id)=0
AND BITAND(k1.m, k4.id)=0
AND BITAND(k1.m, k5.id)=0
AND BITAND(k1.m, k6.id)=0
AND BITAND(k2.m, k3.id)=0
AND BITAND(k2.m, k4.id)=0
AND BITAND(k2.m, k5.id)=0
AND BITAND(k2.m, k6.id)=0
AND BITAND(k3.m, k4.id)=0
AND BITAND(k3.m, k5.id)=0
AND BITAND(k3.m, k6.id)=0
AND BITAND(k4.m, k5.id)=0
AND BITAND(k4.m, k6.id)=0
AND BITAND(k5.m, k6.id)=0
;
COUNT(*)
----------
62266
Elapsed: 00:00:00.66
BITAND只支持两个操作数,换成十进制加法(因为没有十进制位操作,改用字符串方法,效率很低):
WITH k AS (
SELECT POWER(10,LEVEL-1) id ------- 十进制的一位
,TRUNC((LEVEL-1)/6) X
,MOD(LEVEL-1,6) Y
FROM DUAL
CONNECT BY LEVEL<=6*6
)
,king AS (
SELECT k1.id,SUM(k2.id) AS m
FROM k k1,k k2
WHERE k1.id<>k2.id
AND ABS(k1.x-k2.x)<=1 AND ABS(k1.y-k2.y)<=1
GROUP BY k1.id
)
SELECT COUNT(*)
FROM king k1,king k2,king k3,king k4,king k5,king k6
WHERE k1.id<k2.id AND k2.id<k3.id AND k3.id<k4.id AND k4.id<k5.id AND k5.id<k6.id
AND INSTR(TRANSLATE(k1.m+k2.m+k3.m+k4.m+k5.m+k6.m,'0123456','0111111') ---- 所有攻击范围掩码相加后,大于1的位全部转换成1, 达到类似BITOR的效果
+k1.id+k3.id+k4.id+k5.id+k6.id ---- 相加后如果2没有出现,则达到类似BITAND=0的效果
,'2')=0;
COUNT(*)
----------
62266
Elapsed: 00:00:08.21 |
|