原创算法题: 把有互斥条件的多个元素分组, 如何保证组的数量最少

2019-10-14 11:08:13 +08:00
 ofooo

元素=[1,2,3,4,5,6,7,8,9] 互斥=[(1,4),(2,5),(1,5),(5,6),(7,8),(3,9),(2,8),(4,5)]

把元素组成 N 个组, 保证互斥元素不在同一个组里, 并且 N 最小

算法渣渣求解, 这种问题应该怎么做呢?

5352 次点击
所在节点    程序员
37 条回复
stebest
2019-10-14 13:20:13 +08:00
对于原始数组,查询互斥条件,先把里面的元素互斥的元素全踢到另一个新建数组。然后对新数组递归操作,生成另一个新数组,直到新数组长度为 0。
aijam
2019-10-14 13:31:44 +08:00
1. 所有元素组成一个 complete graph。
2. 每一对互斥的 pair 对应 graph 里面的一条 edge,一一删除。
3. 答案是:删除 edge 之后得到的 graph 的 connected component 个数。
arrow8899
2019-10-14 13:31:46 +08:00
BFS
aijam
2019-10-14 13:44:38 +08:00
@aijam 发现这个不对。
wutiantong
2019-10-14 13:51:34 +08:00
@aijam

1. 把(a, b) 二元组当作节点,其中 a 不等于 b,总共 n*(n-1)/2 个节点。
2. 在(a, b) 与 (b, c) 之间连线。
3. 从图中删掉所有互斥组合对应的节点。
4. 答案是剩余图的 connected component 个数。
wutiantong
2019-10-14 13:52:34 +08:00
@wutiantong 好像还是不对呢。
wanwenx
2019-10-14 13:54:36 +08:00
秋招的时候遇到了这题,滴滴的笔试,背景是垃圾分类,然后垃圾互斥,问最少的垃圾车需要多少?结果不会,233333
aijam
2019-10-14 14:02:04 +08:00
aijam
2019-10-14 14:05:31 +08:00
@aijam 只能蒙着眼瞎写猜测 greedy 了。。。
wutiantong
2019-10-14 14:06:56 +08:00
你这个 one-pass 遍历得不到最少分组呀。
aijam
2019-10-14 14:09:15 +08:00
@wutiantong 其实我也这么觉得,只是看看 greedy 分组啥效果 :D
arrow8899
2019-10-14 14:10:34 +08:00
# 先把每个元素的互斥元素找出来
1 (4, 5)
2 (5, 8)
3 (9)
4 (1, 5)
5 (1, 2, 4, 6)
6 (5)
7 (8)
8 (2, 7)
9 (3)

# 新建一个空数组,检测数组中是否存在互斥的元素,不存在就放进去
[1] # 1 直接放进去
[1, 2] # 2 和 1 不互斥,放进去
[1, 2, 3] # 3 之和 9 互斥,放进去
[1, 2, 3] [4] # 4 和 1 互斥,新建一个数组
[1, 2, 3] [4] [5] # 5 和 1,4 都互斥,再新建数组
[1, 2, 3, 6, 7] [4, 8, 9] [5] # 然后 6789 同理
answer = 3
# 如果元素是连续的话,可以直接通过数组下标判断是否互斥,会快很多
ofooo
2019-10-14 14:11:47 +08:00
@wanwenx 我要写个小工具其中需要解决这个问题, 没想到真的是个算法题啊.....

我的解法是
先循环每个元素 a, 把全部元素分成 2 组, A 组=包含元素 a 的最多的元素, B 组是其他元素
然后其他元素递归调用这个函数, 这样就获得的以 a 元素为核心的组, 找到数量最少的成组, 返回

然后加了一个临时变量, 保存每一组计算过的元素和最佳成组, 减少计算量

不知道这样算不算解出来了.....
wutiantong
2019-10-14 14:13:54 +08:00
@wutiantong 我觉得我这个思路还可以抢救一下:

0. 首先我们要扩展补充 (complete) 所有(显式或隐含定义的)互斥二元组。
0-1. 比如说,假如给定(1, 2), (2, 3) 为互斥关系,那么(2, 3) 也是一个(隐含的)互斥关系。
0-2 从图的角度来说,任何两个元素只要有路径(path)连接,就应确保它们之间有一条直接的边(edge)。

1. 把(a, b) 二元组当作节点,其中 a 不等于 b,总共 n*(n-1)/2 个节点。

2. 在(a, b) 与 (b, c) 之间连线。

3. 从图中删掉所有(显式或隐含定义的)互斥二元组对应的节点。

4. 答案是剩余图的 connected component 个数。
wutiantong
2019-10-14 14:16:40 +08:00
@aijam 看我上一条的图解法,我觉得好像没问题了。
ofooo
2019-10-14 14:20:43 +08:00
```python3
class Test:
def get_groups(self, items, reject_pairs):
"""
生成由 items 组成的 N 个组, 保证每个组里的元素不互相排斥, 且 N 最小
:param items: [item1, item2, item3, ...] 要成组的全部元素
:param reject_pairs: {(item1, item3), (item2, item9) } 二元组集合, 每个二元组表示一个排斥条件
:return: [ [item1], [item2, item3], [...] ] 二维数组
"""
self.sub_groups = {} # {排序的 item 元组: 最佳成组二维数组} 保存临时结果, 减小计算量
groups = self._get_groups(items, reject_pairs)

return groups

def _get_groups(self, items, reject_pairs):
if len(items) == 1:
return [[items[0]]]
sort_types = tuple(sorted(items))
if sort_types in self.sub_groups:
return self.sub_groups[sort_types]
best_groups = None
for t1 in items:
now_group = [t1] # 可以和 t1 成组的元素组成的组
others = [] # 不能和 t1 成组的元素组成的组
for t2 in items:
if t1 == t2:
continue
for tt in now_group:
if self.is_cross(tt, t2, reject_pairs):
others.append(t2)
break
else:
now_group.append(t2)
if len(others) == 0:
best_groups = [now_group]
self.sub_groups[sort_types] = best_groups
return best_groups
else:
groups = self._get_groups(others, reject_pairs) + [now_group]
if best_groups is None or len(best_groups) > len(groups):
best_groups = groups
self.sub_groups[sort_types] = best_groups
return best_groups

def is_cross(self, t1, t2, cross_pairs):
if (t1, t2) in cross_pairs:
return True
if (t2, t1) in cross_pairs:
return True
return False

```
我上面说的计算过程的代码...如果有问题大家多指出, 谢谢
ngc3242
2019-10-14 14:24:35 +08:00
感觉像是极大团问题,那就有可能是 NP 问题。元素数量不多的话倒是可以考虑状态压缩动态规划或者记忆化搜索。
necomancer
2019-10-14 14:29:26 +08:00
@arrow8899 这个正解
ofooo
2019-10-14 14:32:16 +08:00
@wutiantong 这个图的解法挺巧妙的, 这样的算法复杂度大概是什么量级的呢?
wutiantong
2019-10-14 14:36:46 +08:00
@ofooo 多项式量级。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/609075

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX