回 帖 发 新 帖 刷新版面

主题:关于 排列&组合——深搜解法

近几天在坛子上见人讨论关于排列和组合的求法,我贴出我的程序,请大家过目

在M$中任选K个字母。

排列:
DECLARE SUB dfs (d!)
CLS
CONST maxn = 100
DIM SHARED a(maxn), used(maxn), l, k, m$

INPUT m$, k
l = LEN(m$)
CALL dfs(1)


SUB dfs (d)
IF d - 1 = k THEN
FOR i = 1 TO k: PRINT MID$(m$, a(i), 1); : NEXT i
PRINT
ELSE FOR i =  1 TO l
       IF used(i) = 0 THEN
         used(i) = 1
         a(d) = i
         dfs (d + 1)
         used(i) = 0
       END IF
     NEXT i
END IF
END SUB


组合:
DECLARE SUB dfs (d!)
CLS
CONST maxn = 100
DIM SHARED a(maxn), used(maxn), l, k, m$

INPUT m$, k
l = LEN(m$)
CALL dfs(1)


SUB dfs (d)
IF d - 1 = k THEN
FOR i = 1 TO k: PRINT MID$(m$, a(i), 1); : NEXT i
PRINT
ELSE FOR i = a(d - 1) + 1 TO l    '细心的人也许已经发现,组合和排列只在这一处改动了1个数
       IF used(i) = 0 THEN
         used(i) = 1
         a(d) = i
         dfs (d + 1)
         used(i) = 0
       END IF
     NEXT i
END IF
END SUB


算法为DFS,深度优先搜索。
欢迎发表意见或建议,新手有不明白也欢迎提问。

回复列表 (共34个回复)

31 楼

楼主以前不大能看得惯我做的太复杂的东西的,
认为我的不系统,难以理解,比不上做出来的框架条理.

但是因为QB默认的栈值应该只有2K,就算clear也多不到哪去
一般的递归个上百次已经很难得了,
而我当初正是因为递归的限制,所以才逼不得已转移方向的
下面我就用我的方法来把楼主的框架作了修改,
由于数组定义的自由性,使得并不占用栈空间,所以限制的范围就可以被扩大了:



const maxn=100
dim s(maxn),i( 深度或者说是层数 )

do until 目标结点 or 无解(即结点尽头)
  i(k)=i(k)+1 '[color=FF00FF]相当于数组s()的指针[/color]
  if 方案i(k)符合要求,包含标志检查 then
     k=k+1    '[color=FF00FF]进入下一层[/color]
  elseif 越界
     i(k)=0   '[color=FF00FF]置起始点[/color]
     k=k-1    '[color=FF00FF]返回上一层[/color]
  end if
  if 目标结点 then
     输出解  
     exit do  '[color=FF00FF]视乎你需要多少个解[/color]
  endif
loop  


其实这种方法无论从内存占用运行效率速度堆栈限制各方面都比调用函数递归的要占优
当然你承不承认那又是另外一回事.
只是有一点,我连数据导论都连第一章都没看完,那些什么线性什么的把我的头弄得很痛
这些什么结点的我也不懂,只是乱套用的.
所以什么什么名称的我实在不懂.(我才懒得去管那么多呢)

32 楼

用楼主的例子来举例

排列:
DEFINT A-Z
INPUT m$, kk
l = LEN(m$)
DIM i(kk), s(l)

k = 1
DO UNTIL i(0) > 0
   i(k) = i(k) + 1
   IF i(k) > l THEN
      i(k) = 0
      k = k - 1
      s(i(k)) = 0
   ELSEIF s(i(k)) = 0 THEN
      s(i(k)) = 1
      k = k + 1
      IF k > kk THEN
         k = k - 1
         FOR j = 1 TO k
          PRINT MID$(m$, i(j), 1);
         NEXT
          PRINT ,
      END IF
      s(i(k)) = 0
   END IF
LOOP

组合:

33 楼

组合更简单
连标志都不用了

DEFINT A-Z
INPUT m$, kk
l = LEN(m$)
DIM i(kk)
k = 1
DO UNTIL i(1) > l - kk + 1
   i(k) = i(k) + 1
   IF i(k) > l THEN
      k = k - 1
   ELSE
      k = k + 1
      IF k > kk THEN
         k = k - 1
         FOR j = 1 TO k
          PRINT MID$(m$, i(j), 1);
         NEXT
          PRINT
      ELSE
         i(k) = i(k - 1)
      END IF
   END IF
LOOP

34 楼

如果是单字符全选排列的话,还有一个方法:

a$ = "012345"         '顺序排列好
l = LEN(a$)

DO
  PRINT a$
  GOSUB 10
LOOP
END


10                 '找下一个排列方式
e = l - 1
DO WHILE MID$(a$, e, 1) > MID$(a$, e + 1, 1)
   e = e - 1                                    '找需要调整的位置 e
   IF e < 1 THEN END
LOOP

FOR i = l TO (e + 1) STEP -1                    '找需要交换的位置 i
    IF MID$(a$, i, 1) > MID$(a$, e, 1) THEN EXIT FOR
NEXT
b$ = MID$(a$, i, 1)                             '交换字符
MID$(a$, i, 1) = MID$(a$, e, 1)
MID$(a$, e, 1) = b$

FOR i = (e + 1) TO (l + e + 1) \ 2              '对后面段的字符排顺序
    j = l + e + 1 - i
    b$ = MID$(a$, i, 1)
    MID$(a$, i, 1) = MID$(a$, j, 1)
    MID$(a$, j, 1) = b$
NEXT
RETURN

我来回复

您尚未登录,请登录后再回复。点此登录或注册