这是我在在线门户网站上看到的Facebook面试问题。
给定集合S,找到其总和<= k的所有最大子集。例如,如果S = {1,2,3,4,5}并且k = 7 输出为:{1,2,3} {1,2,4} {1,5} {2,5} {3,4}
提示:
任何想法如何解决这个问题?
答案 0 :(得分:5)
我有一些想法 - 你需要一棵树。
如果你输入{1, 2, 3, 4, 5}
,并且你正在搜索最大子集 - 你应该从最大的数字开始构建一个树,并且总是展开while sum <= k
(所以不要停止4-2,但是下降到1得到4-2-1)。
因此,从5开始的节点将是: 5-1 / 5-2 - 只有那些2的总和<= 7
从4开始: 4-3 / 4-2-1 / 4-1(之前的子集)
从3开始: 3-2-1 / 3-1(之前的子集)
从2:2-1开始(3-2-1的子集)
从1:1开始(2-1的子集)
然后你可以对有效输出进行排序并获得{1,2,3} {1,2,4} {1,5} {2,5} {3,4}
答案 1 :(得分:2)
我知道现在回答已经很晚了,但我想我找到了解决这个问题的简单方法。我们使用回溯来按字典顺序枚举S
的子集,并检查到目前为止生成的sum
子集。
当sum
超过k
时,有趣的部分会出现:我们需要检查生成的subset
是否是之前报告的项目的正确子集。
一种解决方案是保留所有报告的子集并检查包含,但这很浪费。
相反,我们会计算k
和sum
之间的差异。如果e
中有一个元素S
,e not in subset
和e <= (k - sum)
,那么我们生成的集合就是之前报告的子集的正确子集,我们可以安全地跳过它
以下是普通旧C ++中的完整工作程序,展示了这个想法:
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
typedef std::set<int> Set;
typedef std::vector<int> SubSet;
bool seen_before(const Set &universe, const SubSet &subset, int diff) {
Set::const_iterator i = std::mismatch(universe.begin(), universe.end(),
subset.begin()).first;
return i != universe.end() && *i <= diff;
}
void process(const SubSet &subset) {
if (subset.empty()) {
std::cout << "{}\n";
return;
}
std::cout << "{" << subset.front();
for (SubSet::const_iterator i = subset.begin() + 1, e = subset.end();
i != e; ++i) {
std::cout << ", " << *i;
}
std::cout << "}\n";
}
void generate_max_subsets_rec(const Set &universe, SubSet &subset,
long sum, long k) {
Set::const_iterator i = subset.empty()
? universe.begin()
: universe.upper_bound(subset.back()),
e = universe.end();
if (i == e) {
if (!seen_before(universe, subset, k - sum))
process(subset);
return;
}
for (; i != e; ++i) {
long new_sum = sum + *i;
if (new_sum > k) {
if (!seen_before(universe, subset, int(k - sum)))
process(subset);
return;
} else {
subset.push_back(*i);
if (new_sum == k)
process(subset);
else
generate_max_subsets_rec(universe, subset, new_sum, k);
subset.pop_back();
}
}
}
void generate_max_subsets(const Set &universe, long k) {
SubSet subset;
subset.reserve(universe.size());
generate_max_subsets_rec(universe, subset, 0, k);
}
int main() {
int items[] = {1, 2, 3, 4, 5};
Set u(items, items + (sizeof items / sizeof items[0]));
generate_max_subsets(u, 7);
return 0;
}
输出是按字典顺序排列的所有最大子集,每行一个:
{1, 2, 3}
{1, 2, 4}
{1, 5}
{2, 5}
{3, 4}
答案 2 :(得分:1)
这是一个特权问题。最近我找到了关于算法的this website,并且它一直在描绘我的想象力:因此,追随的powerset /组合解决方案。您只需复制,粘贴和运行该程序即可。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Solution {
public static void maximalSubset
(int sum, int[] set, int choose,List<Integer[]> exclusion) {
if(1>choose) return;
int combinationSize = combinationSize(set.length,choose);
int index[]=new int[choose];
Integer subSet[] = new Integer[choose];
for(int i=0; i<choose;i++)
index[i]=i;
for(int i=0; i<combinationSize; i++) {
if(i!=0)
nextCombination(index,set.length);
for(int x=0; x<choose; x++)
subSet[x]=set[index[x]];
if(summation(sum,subSet) && !excluded(subSet,exclusion)) {
System.out.println(Arrays.toString(subSet));
exclusion.add(Arrays.copyOf(subSet,subSet.length));
}
}
maximalSubset(sum,set,choose-1,exclusion);
}//
private static int combinationSize(int n, int r) {
int den,limit;
if(r>n-r) {
den=n-r;
limit=r;
}else {
den=r;
limit=n-r;
}
long result=1;
for(int i=n; i>limit;i--)
result*=i;
for(int i=2; i<=den;i++)
result/=i;
return (int)result;
}//
private static void nextCombination(int[] A, int n) {
int c=A.length;
int i=c-1;
while(n-c+i==A[i])
i--;
A[i]++;
for(int j=i; j<c; j++)
A[j]=A[i]+j-i;
}//
private static boolean summation(int sum, Integer[] S) {
for(int i:S)
sum-=i;
return sum>=0;
}//
private static boolean excluded(Integer[] needle,List<Integer[]> haystack) {
for(Integer[] H: haystack) {
int count=0;
for(int h: H)
for(int n:needle)
if(h==n) {
count++;
break;//it's a set
}
if(count==needle.length)
return true;
}
return false;
}//
public static void main(String[] args) {
int[] S = {1, 2, 3, 4, 5};
int k = 7;
List<Integer[]> exclusion = new ArrayList<Integer[]>();
maximalSubset(k,S,S.length,exclusion);
}
}
答案 3 :(得分:1)
一个古老的问题,但仍然是一个有趣的问题。
这是一个递归的Java 8解决方案,采用“置换”方法。
针对更干净和更短的代码而非性能进行了优化 - 例如,排序和修剪只需要进行一次。
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import java.util.*;
import java.util.stream.Collectors;
public class SubsetFinder {
public List<List<Integer>> findSubsets(List<Integer> input, int k) {
List<List<Integer>> listOfLists = new ArrayList<>();
List<Integer> copy = Ordering.natural().sortedCopy(input);
while (!copy.isEmpty()) {
int v = copy.remove(copy.size() - 1);
if (v == k || (copy.isEmpty() && v <= k)) {
// No need to look for subsets if the element itself == k, or
// if it's the last remaining element and <= k.
listOfLists.add(new ArrayList<>(Arrays.asList(v)));
} else if (v < k) {
findSubsets(copy, k - v).forEach(subList -> {
subList.add(v);
listOfLists.add(subList);
});
}
}
// Prune sets which are duplicates or subsets of other sets
return listOfLists.stream().filter(
candidate -> listOfLists.stream().noneMatch(
lol -> candidate != lol && lol.containsAll(candidate)
)
).collect(Collectors.toList());
}
}
测试它:
public static void main(String[] args) {
new SubsetFinder()
.findSubsets(ImmutableList.of(1, 2, 3, 4, 5), 7)
.forEach(System.out::println);
}
答案 4 :(得分:1)
算法如下:
Java的工作解决方案如下:
public class Test {
/**
* Assuming alphabet[] is already sorted in increasing order
*/
public static void printMaximalSubSetsToSum(int[] alphabet, int sum) {
if (alphabet == null || alphabet.length == 0) {
return;
}
if (alphabet[0] > sum) {
// no sense to search, since smallest element in array already bigger than sum
return;
} else if (alphabet[0] == sum) {
Set<Integer> subSet = new HashSet<>();
subSet.add(alphabet[0]);
printSubset(subSet);
}
Set<Integer> subSet = new HashSet<>();
processMaximalSubSetToSum(alphabet, sum, 0, 0, subSet);
}
private static void processMaximalSubSetToSum(int[] alphabet, int sum, int sumSoFar, int startFrom, Set<Integer> subSet) {
if (startFrom >= alphabet.length) {
if (isMaximalSubSet(alphabet, subSet, sum - sumSoFar)) {
printSubset(subSet);
}
return;
}
for (int i = startFrom; i < alphabet.length; i++) {
int newSum = sumSoFar + alphabet[i];
if (newSum > sum) {
if (isMaximalSubSet(alphabet, subSet, sum - sumSoFar)) {
printSubset(subSet);
}
return;
} else {
subSet.add(alphabet[i]);
if (newSum == sum) {
printSubset(subSet);
} else {
processMaximalSubSetToSum(alphabet, sum, newSum, i + 1, subSet);
}
subSet.remove(alphabet[i]);
}
}
}
private static boolean isMaximalSubSet(int[] alphabet, Set<Integer> subSet, int diff) {
// search first mismatch element between alphabet and current SubSet
Iterator<Integer> it = subSet.iterator();
int i = 0;
while (it.hasNext()) {
if (it.next() != alphabet[i]) {
break;
}
i++;
}
return i >= alphabet.length || alphabet[i] > diff;
}
private static void printSubset(Set<Integer> subset) {
System.out.println(subset);
}
public static void main(String[] args) throws java.lang.Exception {
//printMaximalSubSetsToSum(new int[]{1, 2, 3, 4, 5}, 7);
// Correct output is: {1, 2, 3}; {1, 2, 4}; {1, 5}; {2, 5}; {3, 4}
}
}
答案 5 :(得分:0)
1)从给定的数组/集
构建MIN-HEAP结构2)从根遍历结构并继续减去您访问的节点的值。一旦超过所需的总和(curr_sum&gt; k),输出此路径,回溯到父路径并采取另一条路径(这可以递归完成)。
3)如果回溯将您带回到您开始的原始节点,请从root-&gt;左侧节点递归实施整个算法。
4)执行上述相同的两个步骤(2)和(3),但现在使用MAX-HEAP。
我是算法和数据结构的新手,并且刚开始阅读Algos-Cormen简介。这可能是一个错误的解决方案,但如果有人向我指出错误,我会非常高兴:)