在一对多关系中至少获取一个父母列表,包括所有孩子一次

时间:2013-03-22 18:28:57

标签: php algorithm

我在报告和位置之间有一对多的关系。我的目标是将我的报告列表缩小到尽可能少的报告,其中包含所有代表的位置。

如果我将其简化为数字列表,它将如下所示,其中键是报告,数组是位置列表:

{
  1:[1,2],
  2:[1],
  3:[2,3],
  4:[1,3,4]
}

理想的解决方案是选择报告1 or 34。可以选择13,因为它们都包含位置2和带有报告1的重复位置4。已选择报告4,因为它是唯一一个位置4

效率不是主要问题。使用PHP缩小列表的最佳方法是什么?

2 个答案:

答案 0 :(得分:3)

NP-completeness再次出现。

您尝试解决的问题称为Set Cover,而且肯定是NP-Complete

这意味着不太可能存在“高效”(读取,多项式时间)算法。

好消息是有简单的近似算法可以给你一个不错的近似值。

请参阅this了解“明显的”贪婪算法(在每个点,选择具有最大数量的未覆盖位置的报告)如何为您提供log (R)近似值,其中R为报告的数量(实际上,它甚至比那更好)。

答案 1 :(得分:1)

如果效率不是你所说的问题,我可以建议你 O(2 ^ n * k)算法,其中 n 是列表的数量和 k 是它们长度的总和。只需使用位掩码进行所有可能的组合,并为每个组合计算它是否涵盖所有内容。

P.S。 这是一个实现(http://ideone.com/bAGpbL):

$arr = array(
  0 => array(1,2),
  1 => array(1),
  2 => array(2,3),
  3 => array(1,3,4),
);
// It is assumed that all indexes are sequential starting from 0
$total_cover = array();
foreach($arr as $sub_arr) {
    foreach($sub_arr as $value) {
        $total_cover[$value] = true;
    }
}
$n = count($arr);
$best_cover = array_keys($arr);
for($i = 0; $i < (1 << $n); $i++) {
    $cover = array();
    $selected_list = array();
    for($j = 0; $j < $n; $j++) {
        if(($i >> $j) & 1) {
            $selected_list[] = $j;
            foreach($arr[$j] as $value) {
                $cover[$value] = true;
            }
        }
    }
    $good_cover = true;
    foreach($total_cover as $key => $value) {
        if(!isset($cover[$key])) {
            $good_cover = false;
            break;
        }
    }
    if($good_cover && count($selected_list) < count($best_cover)) {
        $best_cover = $selected_list;
    }
}
var_dump($best_cover);