我想找到数组中的所有组合以达到给定的总和。例如,如果数组是[1,1,1,2,4,4]并且给定目标是5,那么输出应该是:
Expected Output
1 1 1 2
1 4
1 4
此代码到目前为止我做了:
static void sum(int[] arr, int i, int sum, int target, String s) {
for (int j = i + 1; j < arr.length; j++) {
if (sum + arr[j] == target) {
System.out.println(s + " " + String.valueOf(arr[j]));
} else {
sum(arr, j, sum + arr[j], target, s + " " + String.valueOf(arr[j]));
}
}
}
public static void main(String[] args) {
int[] numbers = { 1, 1, 1, 2, 4, 4 };
for (int i = 0; i < numbers.length; i++) {
sum(numbers, i, numbers[i], 5, String.valueOf(numbers[i]));
}
}
此代码的输出为:
My Output
1 1 1 2
1 4
1 4
1 4
1 4
1 4
1 4
当我们有重复元素时,实际上存在一个问题,它适用于非重复数字,但不存在重复数字时。我想知道如何解决这个问题所以输出看起来像预期的那样。
答案 0 :(得分:2)
我们需要在这个问题中处理的一个基本对象是Bag或MultiSet - 一组可以包含元素的多个实例的无序值。不幸的是,Java Collections框架不支持这一点。而不是试图让List工作,我们可以编写我们自己的非常基本的版本,只需要我们需要的功能。 (Guava库有一个MultiSet,因此您可以查看生产代码)。
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.TreeMap;
public class Bag<E>
{
private Map<E, Integer> m_values;
public Bag()
{
m_values = new TreeMap<E, Integer>();
}
public Bag(Iterable<E> arr)
{
this();
for(E v : arr)
{
add(v);
}
}
public Bag(Bag<E> b)
{
this();
for(E v : b.values())
{
set(v, b.count(v));
}
}
public void add(E v)
{
Integer count = m_values.get(v);
m_values.put(v, count == null ? 1 : count+1);
}
public void add(Bag<E> b)
{
for(E v : b.values())
{
Integer count = m_values.get(v);
m_values.put(v, count == null ? b.count(v) : count+b.count(v));
}
}
public void remove(E v)
{
Integer count = m_values.get(v);
if(count == null) throw new NoSuchElementException();
if(count == 1)
m_values.remove(v);
else
m_values.put(v, count-1);
}
public void remove(Bag<E> b)
{
for(E v : b.values())
{
Integer count = m_values.get(v);
Integer bcount = b.count(v);
if(count == null || count < bcount) throw new NoSuchElementException();
if(count == bcount)
m_values.remove(v);
else
m_values.put(v, count-bcount);
}
}
public boolean contains(Bag<E> b)
{
for(E v : b.values())
{
if(count(v) < b.count(v)) return false;
}
return true;
}
public void set(E v, int count)
{
if(count == 0)
m_values.remove(v);
else
m_values.put(v, count);
}
public int count(E v)
{
Integer count = m_values.get(v);
return count == null ? 0 : count;
}
public Iterable<E> values()
{
return m_values.keySet();
}
public String toString()
{
StringBuilder b = new StringBuilder();
b.append("[");
for(E v : values())
{
for(int i=0; i<count(v); i++)
{
b.append(v + " ");
}
}
b.deleteCharAt(b.length()-1);
b.append("]");
return b.toString();
}
}
解决问题的第一步是生成一个总和为5的候选集列表。我们可以通过从输入数组生成子集来完成此操作,但我们必须注意不要包含重复项。这个代码并不算太糟糕,但实际上,如果效率稍低一些,只生成你感兴趣的计数的所有可能分区,在这种情况下,实际上要容易得多。
import java.util.ArrayList;
import java.util.List;
public class Partition
{
public static List<Bag<Integer>> partitions(int n)
{
return new Partition(n).partitions;
}
private List<Bag<Integer>> partitions;
private Bag<Integer> current;
private Partition(int n)
{
partitions = new ArrayList<>();
current = new Bag<Integer>();
build(n, n);
}
private void build(int n, int max)
{
if (n == 0)
{
partitions.add(0, new Bag<Integer>(current));
}
for (int i = Math.min(max, n); i >= 1; i--)
{
current.add(i);
build(n - i, i);
current.remove(i);
}
}
public static void main(String[] args)
{
for (Bag<Integer> b : partitions(5))
{
System.out.println(b);
}
}
}
输出:
[1 1 1 1 1]
[1 1 1 2]
[1 2 2]
[1 1 3]
[2 3]
[1 4]
[5]
现在我们可以编写一个递归例程,从输入中提取这些分区的最大集合。唯一棘手的部分是确保当我们找到一个集合时它不是我们已经看到的解决方案的子集,在这种情况下我们可以忽略它。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Dice
{
public static List<List<Bag<Integer>>> picks(Integer[] diceArr, int k)
{
return new Dice(diceArr, k).output;
}
private List<List<Bag<Integer>>> output;
private List<Bag<Integer>> current;
private List<Bag<Integer>> partitions;
private Bag<Integer> dice;
private Dice(Integer[] diceArr, int k)
{
output = new ArrayList<>();
current = new ArrayList<>();
partitions = Partition.partitions(5);
dice = new Bag<>(Arrays.asList(diceArr));
build(0);
}
private void build(int pos)
{
for (int i=pos; i<partitions.size(); i++)
{
Bag<Integer> partition = partitions.get(i);
if(dice.contains(partition))
{
dice.remove(partition);
current.add(partition);
build(i);
current.remove(partition);
dice.add(partition);
}
}
// Only add the current list of partitions if we haven't already seen it
if(!current.isEmpty())
{
boolean seen = false;
for(List<Bag<Integer>> prev : output)
{
if(prev.containsAll(current))
{
seen = true;
break;
}
}
if (!seen) output.add(new ArrayList<>(current));
}
}
public static void main(String[] args)
{
int count = 5;
Integer[] dice = {1, 1, 1, 2, 4, 4};
List<List<Bag<Integer>>> picks = picks(dice, count);
for(List<Bag<Integer>> pick : picks)
{
System.out.println(pick);
}
}
}
{1,1,1,2,4,4}的输出:
[[1 1 1 2]]
[[1 4], [1 4]]
{1,1,1,2,3,4,4,4,5}的输出:
[[1 1 1 2], [5]]
[[1 1 3], [1 4], [5]]
[[2 3], [1 4], [1 4], [1 4], [5]]
答案 1 :(得分:-1)
您可以使用地图来保存结果。如果有重复的结果。地图不会保存它。