编辑:
下面的解决方案使用蛮力方法来生成powersets的 powersets(尽管经过修剪)。然后检查满足条件集的组(即将所有数字分组,使得没有一个组包含超过 60 的总和)。我从PMTK3工具箱中的powerset.m
函数中借用了一些代码。
对于像这样的小问题,这应该可以正常工作,但我怀疑对于更大的输入,它的大小会呈指数增长。我确信那里有更好的启发式/算法,所以以此为起点......
%# set of numbers
S = [10,20,30,40,50,60];
%# powerset of S (exclude empty set)
b = (dec2bin(2^numel(S)-1:-1:1) == '1');
P = cellfun(@(idx)S(idx), num2cell(b,2), 'UniformOutput',false);
%# keep only sets where the sum is at most 60
P = P(cellfun(@sum,P) <= 60);
%# take the powerset of the powerset, although we can
%# reduce it to no more than numel(S) subsets in each.
%# The idea here is: nchoosek(p,1:numel(s))
b = (dec2bin(2^numel(P)-1:-1:1) == '1');
b = b(sum(b,2)<=numel(S),:);
PP = cellfun(@(idx)P(idx), num2cell(b,2), 'UniformOutput',false);
%# condition: every number appears exactly once in groups
ppp = cellfun(@(x) [x{:}], PP, 'UniformOutput',false);
idx = find(cellfun(@numel,ppp) == numel(S));
idx2 = ismember(sort(cell2mat(ppp(idx)),2), S, 'rows');
PP = PP( idx(idx2) );
%# cleanup, and show result
clearvars -except S PP
celldisp(PP)
这给了我 12 个解决方案。例如:
>> PP{1}{:}
ans =
10 20 30
ans =
40
ans =
50
ans =
60
>> PP{6}{:}
ans =
10 40
ans =
20
ans =
30
ans =
50
ans =
60
>> PP{12}{:}
ans =
10
ans =
20
ans =
30
ans =
40
ans =
50
ans =
60