|
|
圆桌问题:
一群人围着圆桌而坐。他们起来喝完咖啡,随机坐回座位,发现相邻的六人(左边三位和右边三位)和先前完全不同。满足这种情形的最少人数是多少?
用11GR2的SQL模拟一下,因为最少要13人,从N=13开始把N逐步增加, 发现N=17的时候有了答案:
VAR N NUMBER;
EXEC :N := 17;
WITH t (n,str,cnt,first2,first3,last2,last3) AS (
SELECT 1,CAST(',1,' AS VARCHAR2(1000)),1,-9999,-9999,-9999,-9999 FROM DUAL
UNION ALL
SELECT rn
,t.str||rn||','
,t.cnt+1
,DECODE(t.cnt,1,rn,t.first2)
,DECODE(t.cnt,2,rn,t.first3)
,t.n
,t.last2
FROM t,(SELECT ROWNUM+1 rn FROM DUAL CONNECT BY ROWNUM<:N)
WHERE INSTR(t.str,','||rn||',')=0
AND t.cnt<:N
AND NOT (ABS(rn-t.n)<=3
OR ABS(rn-t.last2)<=3
OR ABS(rn-t.last3)<=3
OR (t.cnt>=:N-3 OR t.cnt<4) AND rn IN (2,3,4,:N,:N-1,:N-2)
OR t.cnt>=:N-2 AND ABS(rn-t.first2)<=3
OR t.cnt =:N-1 AND ABS(rn-t.first3)<=3
OR rn=2 AND (n>:N-2 OR t.last2>:N-2 OR t.last3>:N-2 )
OR rn=3 AND (n>:N-1 OR t.last2>:N-1 OR t.last3>:N-1 )
OR rn=:N-1 AND (n=2 OR t.last2=2 OR t.last3=2 )
OR rn=:N AND (n<4 OR t.last2 IN (2,3) OR t.last3 IN (2,3))
)
)
SELECT TRIM(BOTH ',' FROM str) FROM t WHERE cnt=:N
;
另一类似写法:
WITH d AS (
SELECT ROWNUM+1 rn
,ROWNUM+1-3 min1
,ROWNUM+1+3 max1
,CASE WHEN ROWNUM+1<=3 THEN :N + ROWNUM+1 -3
WHEN ROWNUM+1>=:N-2 THEN 1
ELSE 0
END min2
,CASE WHEN ROWNUM+1<=3 THEN :N
WHEN ROWNUM+1>=:N-2 THEN ROWNUM+1 + 3 - :N
ELSE 0
END max2
FROM DUAL CONNECT BY ROWNUM<:N
)
,t (n,str,cnt,first2,first3,last2,last3) AS (
SELECT 1,CAST(',1,' AS VARCHAR2(1000)),1,-9999,-9999,-9999,-9999 FROM DUAL
UNION ALL
SELECT rn
,t.str||rn||','
,t.cnt+1
,DECODE(t.cnt,1,rn,t.first2)
,DECODE(t.cnt,2,rn,t.first3)
,t.n
,t.last2
FROM t,d
WHERE INSTR(t.str,','||rn||',')=0
AND t.cnt<:N
AND NOT ( t.n BETWEEN d.min1 AND d.max1 OR t.n BETWEEN d.min2 AND d.max2
OR t.last2 BETWEEN d.min1 AND d.max1 OR t.last2 BETWEEN d.min2 AND d.max2
OR t.last3 BETWEEN d.min1 AND d.max1 OR t.last3 BETWEEN d.min2 AND d.max2
OR t.cnt>=:N-3 AND (1 BETWEEN d.min1 AND d.max1 OR 1 BETWEEN d.min2 AND d.max2)
OR t.cnt>=:N-2 AND (t.first2 BETWEEN d.min1 AND d.max1 OR t.first2 BETWEEN d.min2 AND d.max2)
OR t.cnt =:N-1 AND (t.first3 BETWEEN d.min1 AND d.max1 OR t.first3 BETWEEN d.min2 AND d.max2)
)
)
SELECT TRIM(BOTH ',' FROM str) FROM t WHERE cnt=:N
;
LTRIM(REPLACE(STR,''),',')
------------------------------------------------
1,5,9,13,17,4,8,12,16,3,7,11,15,2,6,10,14
1,14,10,6,2,15,11,7,3,16,12,8,4,17,13,9,5
两个答案其实是一样的,只是顺时针和逆时针而已。
[ 本帖最后由 newkid 于 2010-7-24 04:12 编辑 ] |
|