Python 3.x 基于动态规划的数组划分
我应该对两分区问题的动态规划实现进行哪些修改,以解决以下任务: 您将获得一个正整数数组作为输入,表示为C。程序应决定是否可以将该数组划分为两个等和子序列。您可以从数组中删除一些元素,但不能全部删除,以便使这种分区可行 示例: 假设输入是4511179。如果我们去掉11和17,两个分区是可能的。我的问题是,我应该对我的两个分区实现进行哪些调整,以确定两个分区是否可行(可能需要也可能不需要删除某些元素),或者即使删除了某些元素,两个分区是否不可能。程序应在O(总和^2*C)时间内运行 下面是我在Python中的两个分区实现:Python 3.x 基于动态规划的数组划分,python-3.x,algorithm,dynamic-programming,Python 3.x,Algorithm,Dynamic Programming,我应该对两分区问题的动态规划实现进行哪些修改,以解决以下任务: 您将获得一个正整数数组作为输入,表示为C。程序应决定是否可以将该数组划分为两个等和子序列。您可以从数组中删除一些元素,但不能全部删除,以便使这种分区可行 示例: 假设输入是4511179。如果我们去掉11和17,两个分区是可能的。我的问题是,我应该对我的两个分区实现进行哪些调整,以确定两个分区是否可行(可能需要也可能不需要删除某些元素),或者即使删除了某些元素,两个分区是否不可能。程序应在O(总和^2*C)时间内运行 下面是我在Py
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]
创建一个三维数组,该数组按第一个分区的和、第二个分区的和以及元素的数量索引。
T[i][j][k]
如果有可能在第一个k
元素中有两个不相交的子集,其和分别为i
和j
为了计算它,你需要考虑每个元素的三种可能性。要么出现在第一盘,要么出现在第二盘,要么完全被移除。
在循环中为每个可能的总和组合执行此操作将在O(sum^2*C)
中生成所需的数组
要找到问题的答案,您只需检查是否存在某种总和i
,从而T[i][i][n]
为真。这意味着根据问题的要求,有两个不同的子集,它们的总和为i
如果您需要找到实际的子集,那么使用简单的回溯函数很容易找到。只需在back_track函数和recurse中检查这三种可能性中的哪一种是可能的
下面是一个示例实现:
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]))
我很快修改了搜索给定问题的三个等额子集的代码 算法尝试将每件物品
A[idx]
放入第一个袋子,或放入第二个袋子(两个都是真袋子)或第三个(假)袋子(忽略的物品)。实际行李中的初始值(可用空间)为总值的一半。这种方法具有指数复杂性(3^N叶决策树)
但是有很多重复分布,所以我们可以记住一些状态,而忽略分支,所以使用了一种DP记忆。这里提到的状态是当我们使用从最后一个索引到idx
的项目时,真实行李中的可用空间集
状态存储的可能大小可能达到N*sum/2*sum/2
正在运行的Delphi代码(未经过彻底测试,似乎有一个错误,输出的项目被忽略)
函数Solve2(A:TArray):字符串;
变量
地图:t词典;
列表:TStringList的数组;
发现:布尔型;
s2:整数;
函数checkSubsetWithItem(Subs:TArray;idx:Int16):布尔;
变量
键:字符串;
i:整数;
开始
如果(Subs[0]=Subs[1])和(Subs[0]s2),则开始
发现:=真;
退出(真);
结束;
如果idx<0,则
退出(假);
//调试映射包含显式表示的当前剩余总和
键:=格式('%d_%d_%d',[subs[0],subs[1],idx]);
如果Map.ContainsKey(键),则
//回忆录
结果:=映射项[键]
否则开始
结果:=假;
//尝试将[idx]放入第一个、第二个包或忽略它
对于i:=0到2,开始
如果Subs[i]>=A[idx],则开始
Subs[i]:=Subs[i]-A[idx];
结果:=CheckSubsetWithItem(Subs,idx-1);
如果有结果,那么开始
//在递归解卷时检索子集本身
如果找到了
列出[i]。添加(A[idx].ToString);
打破
结束
其他的
//在下次尝试之前重置总和
Subs[i]:=Subs[i]+A[idx];
结束;
结束;
//记住结果记忆
Map.add(键、结果);
结束;
结束;
变量
n、 和:整数;
潜艇:焦油;
开始
n:=长度(A);
总和:=总和(A);
s2:=第2部分之和;
发现:=假;
Map:=TDictionary.Create;
设置长度(列表,3);
列表[0]:=TStringList.Create;
列表[1]:=TStringList.Create;
列表[2]:=TStringList.Create;
如果checkSubsetWithItem([s2,s2,sum],n-1),则开始
结果:='['+列出[0]。CommaText+'],'+
“['+列出[1].CommaText+'],”+
'已忽略:['+列表[2]。CommaText+']';
结束其他
结果:='不走运:(';
结束;
开始
备忘录1.行。添加(解算2([1,5,4,3,2,16,21,44,19]);
备忘录1.行。添加(解算2([1,3,9,27,812437296561]);
结束;
[16,21,19],[1,5,4,2,44],忽略:[3]
运气不好:(
若要确定是否可能,请在这两部分之间保留一组唯一的差异。对于每个元素,迭代到目前为止看到的差异;减去并添加元素。我们正在查找差异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
从确定表的维数开始。您当前的算法需要一个C*s/2表,复杂性为O(C*s)。所需的算法应该具有O(C*s*s)复杂性,因此…请澄清[2,3,1]的预期输出以及原因。@因此可以将数组划分为两个相等和的子数组。在这种情况下,我们不需要删除任何元素。在这种情况下,我会将“子数组”改为“子集”或“子序列”我认为子数组大部分被理解为是连续的。@1490㪞עבקןdone.@1490㪞עבקןקן哎呀,函数返回true或false,所以我完全错过了这一部分。通过回溯成功的I&j可以很容易地完成。如果T1[I][j-1]也是true,则跳过第j个元素,否则就是ne
> f([2, 3, 7, 2])
=> True
> f([2, 3, 7])
=> False
> f([7, 1000007, 1000000])
=> True