为什么我的子集和方法不正确?
Why is my approach to subset sum incorrect?
我是动态编程的新手,并提出了我的(显然不正确的(子集和问题的方法。我想知道为什么我的方法不正确。特别是我很好奇基本的想法是否有效,或者我是否应该坚持使用子集和的正常方法,请参阅 yt。
问题:给定一个数字数组,在数组中找到 2 个子集,这两个子集的总和相同。与正常的子集和问题相比,这个问题略有变化。
示例:[1,5,5,9]
可以分为[1,9]
和[5,5]
。
想法:
1 5 5 9
0 5 5 9
1 1 6 6 10
5 6 5 6 10
5 6 10 10 10
9 6 10 10 10
与其跟踪我采用哪些元素,哪些元素不(像往常一样(,我想跟踪总和。这个想法是在mem[i-1][j]
(在当前位置上方一个(找到先前元素的总和。如果该值 + 当前值小于或等于总和的一半(在本例中为 20(,我们将当前值添加到总和中。否则,我们只是取以前的值而忽略当前值。
表格中对角线上的元素将只是其自身。我这样做是因为否则我会添加相同的元素两次。
在示例中,算法将在看到前 10 个时终止。
实现:
玩转代码
bool has_solution(std::vector<int> &v) {
const long long sum = accumulate(v.begin(), v.end(), 0);
long long mem[v.size() + 1][v.size()];
for (int j = 0; j < v.size(); ++j) {
mem[0][j] = v.at(j);
}
mem[0][0] = 0;
for(int i = 1; i < v.size(); ++i) {
for (int j = 0; j < v.size(); ++j) {
if (i - 1 == j) {
mem[i][j] = v.at(i - 1);
} else {
const long long new_sum = mem[i - 1][j] + v.at(i - 1) ;
if (new_sum <= sum - new_sum) {
mem[i][j] = new_sum;
} else {
mem[i][j] = mem[i - 1][j];
}
}
if (mem[i][j] * 2 == sum) {
return true;
}
}
}
return false;
}
该算法为输入提供了不正确的解决方案[987, 856, 743, 491, 227, 365, 859, 936, 432, 551, 437, 228, 275, 407, 474]
.根据站点,它应该返回 true,但它返回 false。
除了非标准可变长度数组的问题(参见Heart的评论(,你的概念行不通。
缺陷是代码没有考虑必须跳过两个或多个小值才能找到解决方案的可能性。(另请注意,您显示的代码和表不匹配。代码从不执行行1 1 6 6 10
(。
例如,考虑以下顺序:
{4, 1, 6, 3, 4}
唯一有效的分区是{4, 1, 4}
和{6, 3}
。此分区需要跳过两个分区的两个小条目,这是不支持的。
运行如下所示:
| 4 1 6 3 4
--+-------------------
4 |(0) 1 6 3 4
1 | 1 (1) 7 4 5
6 | 7 7 (7) 4 5
3 | 7 7 7 (7) 8
4 | 7 7 7 7 (8)
每列都有其问题。
- (使用 4( 跳过前 4 个,因此它放弃了
{4, 1, 4}
子集。所以它应该跳过1
,但由于它添加了它,此列将不起作用。 - (带 1( 添加
1
,因此只能扩展到{4, 1, 4}
。所以它应该跳过6
,但它最后补充说...... - (6(添加
6
,因此它只能扩展到{4, 1, 4}
,然后添加1
,这是可以的。但是它增加了3
,因为它足够小(总和 4+1+3 == 8(。但是3
它应该跳过它。 - (使用 3( 添加
3+1+4
<9,代码接受该集合,但不会导致有效集合。 - (带4(加
4+1+3
<9,同上。
如果要使用动态编程,则必须按照您发布的链接进行操作。为此,您需要一个包含sum(v) / 2 + 1
元素的vector<bool>
,在第一个元素上使用true
初始化它,并在其余元素上false
。您可以仅使用一个vector
,因为您只对找出是否有任何解决方案感兴趣,而不是返回它。
您的复杂度将是 O(sum(v( * v.size(((,如果值很大,则可能太多。
如果值很大,则可以改用std::unordered_set<int>
来编写与上述vector<bool>
相同的代码,但很少。复杂性更难推理。最坏的情况是每个子集都有不同的总和(例如 {1, 2, 4,..., 2 N-2}, K>>>2 N-1}(。这将使成本 O(2N( 其中 N = v.size((。这比第一种算法要好,第一种算法的成本为O(K(,在这种情况下比O(2N(差得多。
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- C++从另一个类访问公共静态向量的正确方法是什么
- 在 c++ 中拥有一组结构的正确方法是什么?
- 使用2个键的cpp-stl::优先级队列排序不正确
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 正弦级数方程计算不正确
- 为什么我的子集和方法不正确?
- 这种针对 N Queen 问题的回溯方法不正确吗?
- 在不同于依赖DLL的文件夹中输出已编译目标的正确方法是什么
- 在C++中扩展容器(如std::vector)功能的正确方法,而不从中继承
- 返回不可变的新实例的正确方法是什么
- 使用c++stl迭代器而不是传统指针的正确方法是什么
- 从链表中删除节点的方法不正确
- 在mongo中存储大文件(但不是媒体)的正确方法
- 带有可变参数的c++方法(..)报告不正确的参数值
- 根据类型大小增加void*的正确方法,因为强制转换导致临时值,而不是左值
- 将(许多!)数字转换为字符串而不在Qt中进行分配的正确方法
- C++:在不删除指向的对象的情况下,从对象指针向量中删除元素的正确方法是什么
- 方法自动检测find_first_of()和族不正确的情况
- 迭代C++ std::map 的正确方法不起作用