给定 n 个盒子和 x 个球,求组合方式。

7

我正在一个项目上工作,目前有三个盒子,每个盒子都会有一些彩色的小球。

因此,我将它们存储在下面所示的字符串和字符串列表Map中。

Map<String, List<String>> boxBallMap = new LinkedHashMap<String, List<String>>();

上述地图中的数据可能是这样的 -

{box1=[blue, red, orange]}
{box2=[blue, red]}
{box3=[blue, red, orange]}

可能的球在盒子里的组合方式可以是 -

(点A) :: 所有盒子都有相同数量的球 -

{box1=[blue, red, orange]}
{box2=[blue, red, orange]}
{box3=[blue, red, orange]}

or

(B点) :: 任何一个盒子都没有球。假设盒子3里没有球 -

{box1=[blue, red, orange]}
{box2=[blue, red, orange]}
{box3=[]}

or

(点C) :: 有些盒子里的球数量较少。比如说,假设盒子2只有两个球 -

{box1=[blue, red, orange]}
{box2=[blue, red]}
{box3=[blue, red, orange]}

or

(点D) :: 任何一个盒子都没有任何球。假设盒子3和盒子2没有任何球 -

{box1=[blue, red, orange]}
{box2=[]}
{box3=[]}

问题陈述:-

根据上述输入,我需要返回一个映射,它将是 List<Map<String, String>>,假设对于(A点),下面的映射将作为输出返回 -

[{box1=blue, box2=red, box3=orange}, 
{box1=red, box2=orange, box3=blue}, 
{box1=orange, box2=blue, box3=red}]

在这里,如果您看到,每一行的每个盒子都有不同颜色的球 - 意思是盒子1为蓝色盒子2为红色盒子3为橙色。我不能在每一行中使用相同颜色的球。因此,这种组合是不可能的,因为它在两个盒子中有相同颜色的球。

{box1=blue, box2=blue, box3=orange}
并且,在第二行中,我不会使用那些已经在第一行中使用过的球放入该盒子。 输出组合是基于输入传递而生成的,如(A点)所示。 现在,假设对于(B点)作为输入,其中box3没有任何球,我将返回另一个映射,如下所示,它也将是List<Map<String,String>>类型。
[{box1=blue, box2=red}, 
{box1=red, box2=orange}, 
{box1=orange, box2=blue}]
在上面的输出中,您可以看到没有box3,因为没有输入,但是每行中的box1和box2都有交替颜色的球。 现在,假设对于输入(点C),其中box2仅有两种球的颜色,我将返回另一个映射,如下所示,它也将是List<Map<String, String>> -
[{box1=blue, box2=red, box3=orange}, 
{box1=red, box3=blue}, 
{box1=orange, box2=blue, box3=red}]
在上面的输出中,您可以看到第二行没有 box2,因为 box2 只有红色和蓝色的球,为了使组合正确,将 box2 安排在第一行和第三行,以保持每一行球颜色交替的规则。 现在我不明白如何编写这样的方法,该方法可以根据我传递的输入返回映射? 注意:现在盒子始终为三个,但球可能会按上述输入变化。 对此任何建议都将非常有帮助。谢谢。 更新: 我的基本问题是给定球和盒子的输入(如上所示)- 如何返回映射,以确保在每一行中,盒子使用交替/不同颜色的球,并且他们需要确保在前一行中,相同的盒子没有使用那些颜色的球。 对于 (C 点) 作为一个输入,其中 box2 只有两种颜色的球,我想返回如下所示的映射,它也将是 List> 类型的 -
[{box1=blue, box2=red, box3=orange}, 
{box1=red, box3=blue}, 
{box1=orange, box2=blue, box3=red}]
  • 在第一行中,盒子1是蓝色的盒子2是红色的盒子3是橙色的,里面有交替颜色的球。
  • 在第二行中,盒子1是红色的,为什么?因为 蓝色 已经在第一行的盒子1中使用了,而第二行的盒子3是蓝色的,没有 盒子2
  • 第三行同理。

我之前提供的解决方案假设每个盒子里的球的数量总是相同的 -

public List<Map<String, String>> createMappings(List<String> boxes, List<String> balls) {
    List<Map<String, String>> result = new ArrayList<Map<String, String>>();
    for(int i = 0; i < balls.size(); i++) {
        Map<String, String> row = new HashMap<String,String>();
        for(int j = 0; j < boxes.size(); j++) {
            String box = boxes.get(j);
            int ballIndex = (j + i) % balls.size();
            String ball = balls.get(ballIndex);
            row.put(box, ball);
        }
        result.add(row);
    }
    return result;
}
如果我们能修改这个程序,使其开始接受我的输入作为Map,并处理当球的数量可以不同的情况,那么对我来说就会变得非常容易。 更新: 如果我尝试使用下面的输入组合,则输出为空,这是错误的。
List<String> balls1 = Arrays.asList();
List<String> balls2 = Arrays.asList();
List<String> balls3 = Arrays.asList("red", "blue");


Map<String, List<String>> maps = new LinkedHashMap<String, List<String>>();
maps.put("box3", balls3);
maps.put("box2", balls2);
maps.put("box1", balls1);

List<Map<String, String>> mappings = generateMappings(maps);

// below mappings is coming as empty somehow which is wrong
System.out.println(mappings);
但是对于上述输入,输出应该如下所示 -
[{box3=red}, {box3=blue}]
而且,它也不能处理以下输入 -
List<String> balls1 = Arrays.asList("red", "blue", "orange");
List<String> balls2 = Arrays.asList("red", "blue", "orange");
List<String> balls3 = Arrays.asList("red", "blue", "orange", "purple", "pink");
使用上述输入组合,我可以在其他行中看到相同颜色的球,这违反了第三个规则。 更新: 我的规则是: 1. 每一行的框中应该有交替颜色的球。如果您查看上面的内容,每行对于每个框都有交替的球颜色 - 意味着第一行的box1是蓝色,box2是红色,box3是橙色。 2. 其次,我不能在每行中使用同样颜色的球。因此下面的组合是不可能的,因为它在一行中有两个盒子有相同颜色的球。 {box1 = blue,box2 = blue,box3 = orange} 3. 第三,在下一行中,我不会使用早期行中使用过的箱子中的球。因此第二行不能使用blue作为box1,因为它已经由box1在第一行中使用过。 最终代码应该像这样 -
public static List<Map<String, String>> create(Map<String, List<String>> input) {
List<Map<String, String>> output = new ArrayList<Map<String, String>>();
// find all boxes
List<String> boxes = new ArrayList<String>(input.keySet());

// find all colors
Set<String> distinctColors = new LinkedHashSet<String>();
for (List<String> e : input.values()) {
    for (String color : e) {
    if (!distinctColors.contains(color)) {
        distinctColors.add(color);
    }
    }
}
List<String> colors = new ArrayList<String>(distinctColors);

Set<String> generationHistory = new LinkedHashSet<String>();
int colorIndex = 0;
for(int i = 0; i < colors.size(); i++) {
    Map<String, String> row = new LinkedHashMap<String, String>();
    output.add(row);
    colorIndex = i;
    for(int j = 0; j < colors.size(); j++) {
    int boxIndex = j;
    if(boxIndex >= boxes.size()) {
        boxIndex = 0;
    }
    String box = boxes.get(boxIndex);
    List<String> boxColors = input.get(box);
    if(colorIndex >= colors.size()) {
        colorIndex = 0;
    }
    String color = colors.get(colorIndex++);
    // a combination is generated only if the actual
    // colors does exist in the actual box 
    // and it has not already been generated i all previous rows
    if(boxColors.contains(color) && isNotYetGenerated(box, color, generationHistory)) {
        row.put(box, color);
    }
    }
}

return output;
}

private static boolean isNotYetGenerated(String box, String color, Set<String> generationHistory) {
String key = box + "=" + color;
boolean notYetGenerated = !generationHistory.contains(key);
if (notYetGenerated) {
    generationHistory.add(key);
}
return notYetGenerated;
}

2
是的,我能理解.. :).. 想想看,我在格式化问题时所经历的痛苦.. :) - user2467545
你能澄清一下你的基本问题吗?你是在问如何返回一个给定每个盒子彩色球输入的列表吗?你是在询问如何评估这些盒子并回答某些问题(所有盒子里的颜色都相同吗)?还是其他问题?这是关于如何列出“数独”类型的谜题的问题吗?请从更高的层面开始解释:我需要返回xxxx,假设我有xx个颜色球可以放入yy个盒子中。... - ErstwhileIII
@ErstwhileIII:好的,我已经更新了我的问题并添加了一些细节。如果有不清楚的地方,请告诉我。 - user2467545
那么你只需要返回重新排列盒子中球的所有可能变体,以使每一行包含不同颜色的球?如果是这样,最简单但最幼稚的方法是暴力枚举所有可能的组合,排除不匹配的组合。 - svz
如果box1、box2和box3每个盒子里都只有红球,你会期望得到3个映射,即box1=red,box2=red,box3=red吗? - Pace
显示剩余15条评论
3个回答

2

基本上,你需要将所有盒子与所有可能的颜色结合起来。在每一行中,一个盒子得到下一行它在上一行中拥有的颜色。如果您写下所有可能的盒子/颜色组合并编写所有索引,则会更加清晰。PointA是一个完美的例子:

对于输入:

{box1=[blue, red, orange]}
{box2=[blue, red, orange]}
{box3=[blue, red, orange]}

以上输入的所有组合如下(前面带有盒子编号,颜色编号):

0,0 {box1=blue}
0,1 {box1=red}
0,2 {box1=orange}

1,0 {box2=blue}
1,1 {box2=red}
1,2 {box2=orange}

2,0 {box3=blue}
2,1 {box3=red}
2,2 {box3=orange}
您正在寻找以下输出内容:
{box1=blue, box2=red, box3=orange}
{box1=red, box2=orange, box3=blue}
{box1=orange, box2=blue, box3=red}
因此,你要查找的指数如下:
row1    0,0     1,1     2,2
row2    0,1     1,2     2,0
row3    0,2     1,0     2,1

现在你知道你要寻找什么,编写一些循环就变得容易了(免责声明:只要我正确理解了您的问题/尚未完全测试!!!):

public List<Map<String, String>> create(Map<String, List<String>> input) {
    List<Map<String, String>> output = new ArrayList<>();
    // find all boxes
    List<String> boxes = new ArrayList<>(input.keySet());

    // find all colors
    Set<String> distinctColors = new LinkedHashSet<>();
    for(List<String> e : input.values()) {
        for(String color : e) {
            if(! distinctColors.contains(color)) {
                distinctColors.add(color);  
            }
        }
    }
    List<String> colors = new ArrayList<String>(distinctColors);

    int colorIndex = 0;
    for(int i = 0; i < boxes.size(); i++) {
        Map<String, String> row = new LinkedHashMap<>();
        output.add(row);
        colorIndex = i;
        for(int j = 0; j < colors.size(); j++) {
            int boxIndex = j;
            if(boxIndex >= boxes.size()) {
                boxIndex = 0;
            }
            String box = boxes.get(boxIndex);
            List<String> boxColors = input.get(box);
            if(colorIndex >= colors.size()) {
                colorIndex = 0;
            }
            String color = colors.get(colorIndex++);
            // a combination is generated only if the actual
            // colors does exist in the actual box
            if(boxColors.contains(color)) {
                row.put(box, color);    
            }
        }
    }

    return output;
}

这里使用您提供的一些输入进行测试:

点A

@Test
public void createFromPointA() {
    //    {box1=[blue, red, orange]}
    //    {box2=[blue, red, orange]}
    //    {box3=[blue, red, orange]}

    //    [{box1=blue, box2=red, box3=orange}, 
    //     {box1=red, box2=orange, box3=blue}, 
    //     {box1=orange, box2=blue, box3=red}]

    //    0,0 {box1=blue}
    //    0,1 {box1=red}
    //    0,2 {box1=orange}

    //    1,0 {box2=blue}
    //    1,1 {box2=red}
    //    1,2 {box2=orange}

    //    2,0 {box3=blue}
    //    2,1 {box3=red}
    //    2,2 {box3=orange}

    //    0,0   1,1     2,2
    //    0,1   1,2     2,0
    //    0,2   1,0     2,1

    Map<String, List<String>> input = new LinkedHashMap<>();
    input.put("box1", Arrays.asList("blue", "red", "orange"));
    input.put("box2", Arrays.asList("blue", "red", "orange"));
    input.put("box3", Arrays.asList("blue", "red", "orange"));

    List<Map<String, String>> output = create(input);
    for(Map<String, String> e : output) {
        System.out.println(e);  
    }
}

PointB

@Test
public void createFromPointB() {
    //      {box1=[blue, red, orange]}
    //      {box2=[blue, red, orange]}
    //      {box3=[]}

    //      [{box1=blue, box2=red}, 
    //       {box1=red, box2=orange}, 
    //       {box1=orange, box2=blue}]

    //      0,0 {box1=blue}
    //      0,1 {box1=red}
    //      0,2 {box1=orange}

    //      1,0 {box2=blue}
    //      1,1 {box2=red}
    //      1,2 {box2=orange}

    //      2,x {box3=blue}
    //      2,x {box3=red}
    //      2,X {box3=orange}

    //      0,0     1,1     2,x
    //      0,1     1,1     2,x
    //      0,2     1,0     2,x

    Map<String, List<String>> input = new LinkedHashMap<>();
    input.put("box1", Arrays.asList("blue", "red", "orange"));
    input.put("box2", Arrays.asList("blue", "red", "orange"));
    input.put("box3", Collections.<String>emptyList());

    List<Map<String, String>> output = create(input);
    for(Map<String, String> e : output) {
        System.out.println(e);  
    }
}

PointC

@Test
public void createFromPointC() {
    //      {box1=[blue, red, orange]}
    //      {box2=[blue, red]}
    //      {box3=[blue, red, orange]}

    //      [{box1=blue, box2=red, box3=orange}, 
    //       {box1=red, box3=blue}, 
    //       {box1=orange, box2=blue, box3=red}]

    //      0,0 {box1=blue}
    //      0,1 {box1=red}
    //      0,2 {box1=orange}

    //      1,0 {box2=blue}
    //      1,1 {box2=red}
    //      1,x {box2=orange}

    //      2,0 {box3=blue}
    //      2,1 {box3=red}
    //      2,2 {box3=orange}

    //      0,0     1,1     2,2
    //      0,1     1,x     2,0
    //      0,2     1,0     2,1

    Map<String, List<String>> input = new LinkedHashMap<>();
    input.put("box1", Arrays.asList("blue", "red", "orange"));
    input.put("box2", Arrays.asList("blue", "red"));
    input.put("box3", Arrays.asList("blue", "red", "orange"));

    List<Map<String, String>> output = create(input);
    for(Map<String, String> e : output) {
        System.out.println(e);  
    }
}

输出A

{box1=blue, box2=red, box3=orange}
{box1=red, box2=orange, box3=blue}
{box1=orange, box2=blue, box3=red}

OutputB

{box1=blue, box2=red}
{box1=red, box2=orange}
{box1=orange, box2=blue}

OutputC

{box1=blue, box2=red, box3=orange}
{box1=red, box3=blue}
{box1=orange, box2=blue, box3=red}
希望这能帮到你,或者至少在寻找解决方案的路上给你一些提示。 编辑 你可以替换外部for循环。
for(int i = 0; i < boxes.size(); i++) {

使用

for(int i = 0; i < colors.size(); i++) {

这样生成的方案是基于颜色数量而非盒子数量。如果这种方法不能解决其他组合问题,那么您可以在将组合添加到行之前进行检查:

if(boxColors.contains(color) && notYetGenerated()) {
    row.put(box, color);    
}

编辑2

这里是一个isNotYetGenerated方法的示例实现。

private boolean isNotYetGenerated(String box, String color, 
                                  Set<String> generationHistory) {
    String key = box + "=" + color;
    boolean notYetGenerated = ! generationHistory.contains(key);
    if(notYetGenerated) {
        generationHistory.add(key);
    }
    return notYetGenerated;
}
create方法中创建集合并将其传递给该方法。
    Set<String> generationHistory = new LinkedHashSet<>();
    int colorIndex = 0;
    int index = boxes.size() > colors.size() ?  boxes.size() : colors.size();
    for(int i = 0; i < index; i++) {
        Map<String, String> row = new LinkedHashMap<>();
        output.add(row);
        colorIndex = i;
        for(int j = 0; j < index; j++) {
            int boxIndex = j;
            if(boxIndex >= boxes.size()) {
                boxIndex = 0;
            }
            String box = boxes.get(boxIndex);
            List<String> boxColors = input.get(box);
            if(colorIndex >= colors.size()) {
                colorIndex = 0;
            }
            String color = colors.get(colorIndex++);
            // a combination is generated only if the actual
            // colors does exist in the actual box 
            // and it has not already been generated i all previous rows
            if(boxColors.contains(color) && isNotYetGenerated(box, color, generationHistory)) {
                row.put(box, color);
            }
        }
    }

PonitF测试

@Test
public void createFromPointF() {
    //      {box1=red, box2=blue, box3=orange}
    //      {box1=blue, box2=orange, box3=purple}
    //      {box1=red, box3=pink}
    //      {box3=red, box1=orange}
    //      {box3=blue}

    //      0,0    {box1=red}
    //      0,1    {box1=blue}
    //      0,2    {box1=orange}
    //      0,x    {box1=purple}
    //      0,x    {box1=pink}
    //
    //      1,0    {box2=red}
    //      1,1    {box2=blue}
    //      1,2    {box2=orange}
    //      1,x    {box2=purple}
    //      1,x    {box2=pink}
    //
    //      2,0    {box3=red}
    //      2,1    {box3=blue}
    //      2,2    {box3=orange}
    //      2,3    {box3=purple}
    //      2,4    {box3=pink}

    //      0,0     1,1     2,2
    //      0,1     1,2     2,3
    //      0,x     1,x     2,0
    //      0,x     1,0     2,1

    Map<String, List<String>> input = new LinkedHashMap<>();
    input.put("box1", Arrays.asList("red", "blue", "orange"));
    input.put("box2", Arrays.asList("red", "blue", "orange"));
    input.put("box3", Arrays.asList("red", "blue", "orange", "purple", "pink"));

    List<Map<String, String>> output = create(input);
    Assert.assertEquals(
            "{box1=red, box2=blue, box3=orange}\r\n" + 
            "{box1=blue, box2=orange, box3=purple}\r\n" + 
            "{box1=orange, box3=pink}\r\n" + 
            "{box3=red}\r\n" + 
            "{box2=red, box3=blue}\r\n", toString(output));
}

private String toString(List<Map<String, String>> output) {
    StringWriter sw = new StringWriter();
    for(Map<String, String> e : output) {
        sw.write(e.toString());
        sw.write("\r\n");
    }
    return sw.toString();
}

输出文件

{box1=red, box2=blue, box3=orange}
{box1=blue, box2=orange, box3=purple}
{box1=orange, box3=pink}
{box3=red}
{box2=red, box3=blue}

非常感谢A4L的帮助。我在我的测试用例上运行了你的示例,一切都很正常,但是我注意到一个问题,就是如果盒子1中只有两个球,而其他盒子为空,那么我的一个用例会出现问题。我已经更新了这个问题的描述。 - user2467545
感谢A4L的建议...这个方法可行,但我还发现了一个问题,我想我可以自己解决它...非常感谢你的帮助。 - user2467545
在我修复一些问题时,我发现在某些输入组合上当前的代码无法正常工作。我已经更新了问题并提供了这些输入组合。请看看您是否能够找到代码中的问题,否则我可能需要花更多时间来解决这个问题。我已经花费了几个小时,但无法理解代码中的问题是什么。 - user2467545
无论何时你在附近,请告诉我。看一下我们之前的聊天记录。 - user2467545
@SSH,请查看我在聊天室中的回复。 - A4L

0
如果我理解问题正确,您可以执行以下操作:
  1. 按盒子中球的数量排序(从小到大升序)。
  2. 当颜色还有剩余时
  3. 循环遍历已排序的盒子列表
  4. 在每次迭代中从盒子中选择一种颜色(如果还有剩余),该颜色在当前迭代中尚未被选择(while循环)
希望这可以帮助您。没有代码,必须睡觉。 编辑:这是伪代码:
arranged_colors = [] // empty list, this is you desired output
sort_the_boxes(boxes) // ascending, by the number of colors in it
while( there_are_more_colors_left() ) { // a method that is easy to implement
  current_list = [] // empty list
  for( box in boxes ) {
    for( color in box ) {
      if( not color in current_list ) {
        current_list.add(color)
        box.remove(color)
        break
      }
    }
  }
  aranged_colors.add(current_colors)
}

谢谢你。我在实现部分卡住了。逻辑上,我和你指出的方向是一致的,但不知何故无法使其正常工作。目前我所拥有的代码(在问题中展示)只能处理每个盒子中球的数量相同的情况。 - user2467545
谢谢B.J. 我尝试实现你的算法,但由于我不知道如何做一些事情,所以无法完成。我已经卡在这里很久了...如果您能提供一个例子,那对我来说将是非常有帮助的。感谢您的帮助。 - user2467545

0
您可以考虑以下策略(因为这是“作业”,所以不提供代码):
  1. 创建一个具有颜色的Ball
  2. 创建一个Box类,其中包含用于计算球数的计数方法和一个addBall方法
  3. 在Box中创建一个“choose”方法,您可以在其中给出一组球,并从盒子中取出一个与球数组输入中的任何颜色都不匹配或为空的球。

通过创建列(大小为盒子中最大球数),开始制作输出,然后对于第1行,从盒子1中拉出一个没有之前拉出的球颜色的球, 对于第2行,从盒子2中拉出一个没有在该列中出现过的颜色的球)...


这不是一份作业。我在问题中提供了代码,但它假设所有盒子中的球数相同。之前我没有提供这个信息是因为问题变得相当长,所以我避免了它。请看一下。 - user2467545

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,