|
部分和問題
与えられたn個の整数a1,a2,…,anから部分集合をうまく選んで、
その集合内の数の和が与えられた数N(0≦N≦Σak)に等しくなるようにできるかどうかを判定する。
組合せと2進法のビットパターンを対応させる場合、O(2^n)となりますが、
1~Nまでの配列が確保できれば、O(n*N)で算出できます。
!部分和問題(Subset Sum Problem)を動的計画法(Dynamic Programming)で解く
LET C=8 !個数
DATA 1,2,3,4,6,8,12,24 !集合 ※正の整数
DIM D(C)
MAT READ D
LET S=0 !総和 Σak
FOR i=1 TO C
LET S=S+D(i)
NEXT i
DIM W(0 TO S) !整数1~Sを部分集合の和で表す
LET W(0)=0
FOR i=1 TO S
LET W(i)=-1
NEXT i
LET k=1 !集合から1つ要素を取り出す
DO WHILE k<=C
LET M=D(k)
FOR i=S TO 0 STEP -1 !最後からみていき、-1以外のインデックスをiとする
IF W(i)<>-1 THEN
LET t=i+M
IF t<=S AND W(t)=-1 THEN LET W(t)=M
END IF
IF W(S)<>-1 THEN EXIT DO !部分和が見つかれば、終了する
NEXT i
LET k=k+1
LOOP
!!!MAT PRINT W; !debug
FOR i=1 TO S
IF W(i)<>-1 THEN
PRINT i;"= {";
LET T=i !部分和を表示する
DO WHILE T>0
PRINT STR$(W(T));
LET T=T-W(T)
IF T>0 THEN PRINT ",";
LOOP
PRINT "}"
END IF
NEXT i
END
実行結果
1 = {1}
2 = {2}
3 = {2,1}
4 = {3,1}
5 = {3,2}
6 = {3,2,1}
7 = {4,2,1}
8 = {4,3,1}
9 = {4,3,2}
10 = {4,3,2,1}
11 = {6,3,2}
12 = {6,3,2,1}
13 = {6,4,2,1}
14 = {6,4,3,1}
15 = {6,4,3,2}
16 = {6,4,3,2,1}
17 = {8,4,3,2}
18 = {8,4,3,2,1}
19 = {8,6,3,2}
20 = {8,6,3,2,1}
21 = {8,6,4,2,1}
22 = {8,6,4,3,1}
23 = {8,6,4,3,2}
24 = {8,6,4,3,2,1}
25 = {12,6,4,2,1}
26 = {12,6,4,3,1}
27 = {12,6,4,3,2}
28 = {12,6,4,3,2,1}
29 = {12,8,4,3,2}
30 = {12,8,4,3,2,1}
31 = {12,8,6,3,2}
32 = {12,8,6,3,2,1}
33 = {12,8,6,4,2,1}
34 = {12,8,6,4,3,1}
35 = {12,8,6,4,3,2}
36 = {12,8,6,4,3,2,1}
60 = {24,12,8,6,4,3,2,1}
|
|