Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 3.x 基于动态规划的数组划分_Python 3.x_Algorithm_Dynamic Programming - Fatal编程技术网

Python 3.x 基于动态规划的数组划分

Python 3.x 基于动态规划的数组划分,python-3.x,algorithm,dynamic-programming,Python 3.x,Algorithm,Dynamic Programming,我应该对两分区问题的动态规划实现进行哪些修改,以解决以下任务: 您将获得一个正整数数组作为输入,表示为C。程序应决定是否可以将该数组划分为两个等和子序列。您可以从数组中删除一些元素,但不能全部删除,以便使这种分区可行 示例: 假设输入是4511179。如果我们去掉11和17,两个分区是可能的。我的问题是,我应该对我的两个分区实现进行哪些调整,以确定两个分区是否可行(可能需要也可能不需要删除某些元素),或者即使删除了某些元素,两个分区是否不可能。程序应在O(总和^2*C)时间内运行 下面是我在Py

我应该对两分区问题的动态规划实现进行哪些修改,以解决以下任务:

您将获得一个正整数数组作为输入,表示为C。程序应决定是否可以将该数组划分为两个等和子序列。您可以从数组中删除一些元素,但不能全部删除,以便使这种分区可行

示例:

假设输入是4511179。如果我们去掉11和17,两个分区是可能的。我的问题是,我应该对我的两个分区实现进行哪些调整,以确定两个分区是否可行(可能需要也可能不需要删除某些元素),或者即使删除了某些元素,两个分区是否不可能。程序应在O(总和^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]

创建一个三维数组,该数组按第一个分区的和、第二个分区的和以及元素的数量索引。
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