我应该对两个分区问题的动态编程实现进行哪些修改以解决以下任务:
您将得到一个由正整数组成的数组作为输入,用C表示。程序应确定是否有可能将该数组划分为两个相等的和子序列。您可以从数组中删除一些元素,但不能全部删除,以使这种分区可行。
示例:
假设输入为4 5 11 17 9.如果我们删除11和17,则可能有两个分区。我的问题是我应该对我的两个分区实现进行哪些调整,以确定两个分区是否可能(可能或可能不需要)删除某些元素),或者即使删除了某些元素也无法输出两个分区。该程序应在O(sum ^ 2 * C)时间运行。
这是我在Python中的两个分区实现:
def two_partition(C):
n = len(C)
s = sum(C)
if s % 2 != 0: return False
T = [[False for _ in range(n + 1)] for _ in range(s//2 + 1)]
for i in range(n + 1): T[0][i] = True
for i in range(1, s//2 + 1):
for j in range(1, n + 1):
T[i][j] = T[i][j-1]
if i >= C[j-1]:
T[i][j] = T[i][j] or T[i-C[j-1]][j-1]
return T[s // 2][n]
答案 0 :(得分:2)
创建一个三维数组,该数组由第一分区的总和,第二分区的总和和元素数索引。
T[i][j][k]
仅当在第一个i
元素中可能有两个分别为和j
和k
的不相交的子集时才为真。
要计算它,您需要为每个元素考虑三种可能性。它可能存在于第一组或第二组中,或者已被完全删除。
循环执行每种可能的总和组合,在O(sum ^ 2 * C)
中生成所需的数组。
要找到问题的答案,您需要检查的是存在一些i
,使得T[i][i][n]
为真。这意味着根据问题的要求,有两个不同的子集,它们的总和为i
。
如果您需要找到实际的子集,则可以使用简单的回溯功能轻松地做到这一点。只需检查back_track函数中的三种可能性中的哪一种并进行递归即可。
这是一个示例实现:
def back_track(T, C, s1, s2, i):
if s1 == 0 and s2 == 0: return [], []
if T[s1][s2][i-1]:
return back_track(T, C, s1, s2, i-1)
elif s1 >= C[i-1] and T[s1 - C[i-1]][s2][i-1]:
a, b = back_track(T, C, s1 - C[i-1], s2, i-1)
return ([C[i-1]] + a, b)
else:
a, b = back_track(T, C, s1, s2 - C[i-1], i-1)
return (a, [C[i-1]] + b)
def two_partition(C):
n = len(C)
s = sum(C)
T = [[[False for _ in range(n + 1)] for _ in range(s//2 + 1)] for _ in range(s // 2 + 1)]
for i in range(n + 1): T[0][0][i] = True
for s1 in range(0, s//2 + 1):
for s2 in range(0, s//2 + 1):
for j in range(1, n + 1):
T[s1][s2][j] = T[s1][s2][j-1]
if s1 >= C[j-1]:
T[s1][s2][j] = T[s1][s2][j] or T[s1-C[j-1]][s2][j-1]
if s2 >= C[j-1]:
T[s1][s2][j] = T[s1][s2][j] or T[s1][s2-C[j-1]][j-1]
for i in range(1, s//2 + 1):
if T[i][i][n]:
return back_track(T, C, i, i, n)
return False
print(two_partition([4, 5, 11, 9]))
print(two_partition([2, 3, 1]))
print(two_partition([2, 3, 7]))
答案 1 :(得分:1)
要确定是否可能,请在这两个部分之间保留一组唯一的差异。对于每个元素,迭代到目前为止所看到的差异;减去并添加元素。我们正在寻找差异0。
4 5 11 17 9
0 (empty parts)
|0 ± 4| = 4
set now has 4 and empty-parts-0
|0 ± 5| = 5
|4 - 5| = 1
|4 + 5| = 9
set now has 4,5,1,9 and empty-parts-0
|0 ± 11| = 11
|4 - 11| = 7
|4 + 11| = 15
|5 - 11| = 6
|5 + 11| = 16
|1 - 11| = 10
|1 + 11| = 12
|9 - 11| = 2
|9 + 11| = 20
... (iteration with 17)
|0 ± 9| = 9
|4 - 9| = 5
|4 + 9| = 13
|5 - 9| = 4
|5 + 9| = 14
|1 - 9| = 8
|1 + 9| = 10
|9 - 9| = 0
Bingo!
Python代码:
def f(C):
diffs = set()
for n in C:
new_diffs = [n]
for d in diffs:
if d - n == 0:
return True
new_diffs.extend([abs(d - n), abs(d + n)])
diffs = diffs.union(new_diffs)
return False
输出:
> f([2, 3, 7, 2])
=> True
> f([2, 3, 7])
=> False
> f([7, 1000007, 1000000])
=> True
答案 2 :(得分:0)
我迅速修改了代码,以搜索三个等和子集来解决给定的问题。
算法尝试将每个项目A[idx]
放在第一个袋子中,或放在第二个袋子中(均为真实袋子),或放在第三个(假)袋子中(忽略的物品)。实际包装袋中的初始值(可用空间)为总和的一半。这种方法按原样具有指数复杂度(带有3 ^ N个叶子的决策树)
但是有很多重复的分布,所以我们可以记住一些状态而忽略分支,因此使用了一种DP-记忆。这里提到的状态是当我们使用从最后一个索引到idx
的所有内容时,实际包装袋中可用空间的集合。
状态存储的可能大小可能达到N * sum/2 * sum/2
有效的Delphi代码(未经充分测试,似乎存在一个错误,输出的项目被忽略)
function Solve2(A: TArray<Integer>): string;
var
Map: TDictionary<string, boolean>;
Lists: array of TStringList;
found: Boolean;
s2: integer;
function CheckSubsetsWithItem(Subs: TArray<Word>; idx: Int16): boolean;
var
key: string;
i: Integer;
begin
if (Subs[0] = Subs[1]) and (Subs[0] <> s2) then begin
found:= True;
Exit(True);
end;
if idx < 0 then
Exit(False);
//debug map contains current rests of sums in explicit representation
key := Format('%d_%d_%d', [subs[0], subs[1], idx]);
if Map.ContainsKey(key) then
//memoisation
Result := Map.Items[key]
else begin
Result := false;
//try to put A[idx] into the first, second bag or ignore it
for i := 0 to 2 do begin
if Subs[i] >= A[idx] then begin
Subs[i] := Subs[i] - A[idx];
Result := CheckSubsetsWithItem(Subs, idx - 1);
if Result then begin
//retrieve subsets themselves at recursion unwindning
if found then
Lists[i].Add(A[idx].ToString);
break;
end
else
//reset sums before the next try
Subs[i] := Subs[i] + A[idx];
end;
end;
//remember result - memoization
Map.add(key, Result);
end;
end;
var
n, sum: Integer;
Subs: TArray<Word>;
begin
n := Length(A);
sum := SumInt(A);
s2 := sum div 2;
found := False;
Map := TDictionary<string, boolean>.Create;
SetLength(Lists, 3);
Lists[0] := TStringList.Create;
Lists[1] := TStringList.Create;
Lists[2] := TStringList.Create;
if CheckSubsetsWithItem([s2, s2, sum], n - 1) then begin
Result := '[' + Lists[0].CommaText + '], ' +
'[' + Lists[1].CommaText + '], ' +
' ignored: [' + Lists[2].CommaText + ']';
end else
Result := 'No luck :(';
end;
begin
Memo1.Lines.Add(Solve2([1, 5, 4, 3, 2, 16,21,44, 19]));
Memo1.Lines.Add(Solve2([1, 3, 9, 27, 81, 243, 729, 6561]));
end;
[16,21,19], [1,5,4,2,44], ignored: [3]
No luck :(