在这个问题中我们需要处理的基本对象之一是一个Bag或MultiSet - 一组无价值的值,可以包含一个元素的多个实例。不幸的是,Java集合框架不支持这一点。我们可以用我们需要的功能编写我们自己的非常基本的版本,而不是围绕尝试让List工作。 (番石榴图书馆有一个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.我们可以从输入阵列生成的子集做到这一点,但我们必须要小心,不要重复包含。该代码,这是不是太糟糕,但它实际上是容易得多,如果有点效率较低,只产生你感兴趣的计数的所有可能的分区,在这种情况下,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 4'只出现在期望的输出列表中两次? – templatetypedef
你现在得到的输出实际上比你想要的更正确。有6种可能的1 4种组合。 – baao
你的问题并不意味着在输出中有3行,但是7.程序是正确的。 –