我们如何将一个数组分成两个新数组,使它们的权重差异最小?

huangapple 未分类评论72阅读模式
英文:

How could we split an array into two new ones which their weight differences are minimal?

问题

以下是翻译好的内容:

我正在进行以下的编程练习:Stone pile。题目如下:

> 给你一堆不同重量的石头。
>
> 你的任务是将这堆石头分成两部分,使得两堆石头的重量差尽可能小。你不能把一个石头分成多块。
>
> 例如,如果我们有石头:
>
> [1, 2, 3, 4]
>
> 我们可以将它们分成:
>
> [2, 3],[1, 4]
>
> 这样差值就为 0
>
> 即使给你的石头堆是空的,你仍然可以将其分成两堆没有石头的部分。
>
> 这堆石头最多包含 22 颗石头。 🗿

根据我思考的示例,做以下操作:

将石头按升序排列
对于每块石头
 如果数量是偶数
  如果次序是偶数
   将最小和最大的放入右侧列表
   删除它们
  如果次序是奇数
   将最小和最大的放入左侧列表
   删除它们

 如果数量是奇数
  如果次序是偶数
   将最大的放入左侧
   删除它
  如果次序是奇数
   将最小的放入右侧
   删除它
   如果还有更多的项
   添加下一个最小值
   删除它

因此,代码如下:

import java.util.*;
import java.util.stream.*;

public class Pile {
    public static int minDiff(int[] stones) {
      System.out.println("stones: "+Arrays.toString(stones));
      Arrays.sort(stones);
      System.out.println("stones: "+Arrays.toString(stones));
      
      List<Integer> allStones = Arrays.stream(stones).boxed().collect(Collectors.toList());
      System.out.println("allStones: "+Arrays.toString(allStones.toArray()));
      ArrayList<Integer> left = new ArrayList<>();
      ArrayList<Integer> right = new ArrayList<>();

      for(int i = 0; allStones.size()>0; i++){
        if(stones.length%2==0){
          if(i%2==0){
            right.add(allStones.get(0));
            right.add(allStones.get(allStones.size()-1));
            allStones.remove(0);
            allStones.remove(allStones.size()-1);
          }else{
            left.add(allStones.get(0));
            left.add(allStones.get(allStones.size()-1));
            allStones.remove(0);
            allStones.remove(allStones.size()-1);
          }
        }else{
          if(i%2==0){
            left.add(allStones.get(allStones.size()-1));
            allStones.remove(allStones.size()-1);
          }else{
            right.add(allStones.get(0));
            allStones.remove(0);
            if(allStones.size()>0){
              right.add(allStones.get(0));
              allStones.remove(0);
            }
          }
        }
      }
      System.out.println("left: "+Arrays.toString(left.toArray()));
      System.out.println("right: "+Arrays.toString(right.toArray()));
      return left.stream().mapToInt(Integer::intValue).sum()-right.stream().mapToInt(Integer::intValue).sum();
    }
}

目前只能在差值为零的输入测试下正常工作。然而,如果我们使用其他输入进行测试,就会失败。

给出以下测试,第一组测试通过,其余的测试失败:

import org.junit.Test;
import static org.junit.Assert.assertEquals;
import org.junit.runners.JUnit4;

public class PersonTest {
    
    @Test
    public void testDifferenceShouldBeZero(){
        assertEquals(0, Pile.minDiff(new int[]{1, 2, 3}));
        assertEquals(0, Pile.minDiff(new int[]{1, 2, 3, 4}));
        assertEquals(0, Pile.minDiff(new int[]{5,5,4,3,3}));
    }
    
    @Test
    public void testDifferenceShouldBeOne(){
        assertEquals(1, Pile.minDiff(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}));
    }
    
    @Test
    public void testDifferenceShouldBeTwo(){
        assertEquals(2, Pile.minDiff(new int[]{89409, 34351, 3065, 10128, 27694, 27585, 87350, 33875, 2658, 41606, 57512, 73368, 83345, 37048, 31827, 94644, 15972, 74813, 31441, 70395}));
    }
    
}

例如,当石头是:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]

追踪如下:

stones: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]
stones: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]
allStones: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]
left: [2, 21, 4, 19, 6, 17, 8, 15,
英文:

I am doing the following programming exercise: Stone pile. The statement is:

> You are given pile of stones with different weights.
>
> Your task is to divide this stone pile into two parts, so the weight
> difference between the piles will be minimal. You can't break one
> stone into smaller ones.
>
> For example if we have stones:
>
> [1, 2, 3, 4]
>
> We could divide them into:
>
> [2, 3], [1, 4]
>
> So the difference will be 0
>
> If you are given empty stone pile, you can still divide it into two
> piles with no stones.
>
> The pile will contain maximum 22 stones. 🗿

Following the examples I have thought:

Sort stones in ascending order
for each stone
 if length is even
  if movement is even
   add min and max to right list
   remove them
  if movement is odd
   add min and max to left list
   remove them

 if length is odd
  if movement is even
   add max to left
   remove it
  if movement is odd
   add min to right
   remove it
   if there are more items
   add next min
   remove it

So in code would be:

import java.util.*;
import java.util.stream.*;

public class Pile {
    public static int minDiff(int[] stones) {
      System.out.println(&quot;stones: &quot;+Arrays.toString(stones));
      Arrays.sort(stones);
      System.out.println(&quot;stones: &quot;+Arrays.toString(stones));
      
      List&lt;Integer&gt; allStones = Arrays.stream(stones).boxed().collect(Collectors.toList());
      System.out.println(&quot;allStones: &quot;+Arrays.toString(allStones.toArray()));
      ArrayList&lt;Integer&gt; left = new ArrayList&lt;&gt;();
      ArrayList&lt;Integer&gt; right = new ArrayList&lt;&gt;();

      for(int i = 0; allStones.size()&gt;0; i++){
        if(stones.length%2==0){
          if(i%2==0){
            right.add(allStones.get(0));
            right.add(allStones.get(allStones.size()-1));
            allStones.remove(0);
            allStones.remove(allStones.size()-1);
          }else{
            left.add(allStones.get(0));
            left.add(allStones.get(allStones.size()-1));
            allStones.remove(0);
            allStones.remove(allStones.size()-1);
          }
        }else{
          if(i%2==0){
            left.add(allStones.get(allStones.size()-1));
            allStones.remove(allStones.size()-1);
          }else{
            right.add(allStones.get(0));
            allStones.remove(0);
            if(allStones.size()&gt;0){
              right.add(allStones.get(0));
              allStones.remove(0);
            }
          }
        }
      }
      System.out.println(&quot;left: &quot;+Arrays.toString(left.toArray()));
      System.out.println(&quot;right: &quot;+Arrays.toString(right.toArray()));
      return left.stream().mapToInt(Integer::intValue).sum()-right.stream().mapToInt(Integer::intValue).sum();
    }
}

Currently it is just working when we test it with inputs where the difference is zero. However if we test it with other inputs, it will fail.

Given the following tests, the first suite will pass, the others fail:

import org.junit.Test;
import static org.junit.Assert.assertEquals;
import org.junit.runners.JUnit4;

public class PersonTest {
    
    @Test
    public void testDifferenceShouldBeZero(){
        assertEquals(0, Pile.minDiff(new int[]{1, 2, 3}));
        assertEquals(0, Pile.minDiff(new int[]{1, 2, 3, 4}));
        assertEquals(0, Pile.minDiff(new int[]{5,5,4,3,3}));
    }
    
    @Test
    public void testDifferenceShouldBeOne(){
        assertEquals(1, Pile.minDiff(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}));
    }
    
    @Test
    public void testDifferenceShouldBeTwo(){
        assertEquals(2, Pile.minDiff(new int[]{89409, 34351, 3065, 10128, 27694, 27585, 87350, 33875, 2658, 41606, 57512, 73368, 83345, 37048, 31827, 94644, 15972, 74813, 31441, 70395}));
    }
    
}

For example, when stones are: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]

Trace is:

stones: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]
stones: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]
allStones: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]
left: [2, 21, 4, 19, 6, 17, 8, 15, 10, 13]
right: [1, 22, 3, 20, 5, 18, 7, 16, 9, 14, 11, 12]

Being the result:

expected:<1> but was:<-23>

How could we split the stone pile in two chunks where the difference is the minimum, for the general case?

We have read:

答案1

得分: 0

我会使用位组合来将石头分成两组并计算每种可能的组合之和将它们进行比较以选择具有最小差异的组合

import java.util.Arrays;
import java.util.LinkedList;

public class Pile {
    /**
     * 返回int型解决方案,如果位被设置,则使用“bitNumber”作为石头的索引
     * 放到一边,否则放到另一边
     **/
    private static int divide(int[] stones) {
        int solution = -1;
        long minDifference = Long.MAX_VALUE;
        for (int n = 0; n < (1 << (stones.length + 1)); n++) {
            long sumLeft = 0;
            long sumRight = 0;
            for (int bit = 0; bit < stones.length; bit++) {
                boolean isSet = (n & (1 << bit)) > 0;
                if (isSet) {
                    sumLeft += stones[bit];
                } else {
                    sumRight += stones[bit];
                }
            }
            long diff = Math.abs(sumLeft - sumRight);
            if (diff < minDifference) {
                solution = n;
                minDifference = diff;
            }
        }
        return solution;
    }

    public static long minDiff(int[] stones) {
        int solution = divide(stones);
        long sumLeft = 0;
        long sumRight = 0;

        LinkedList<Integer> left = new LinkedList<>();
        LinkedList<Integer> right = new LinkedList<>();
        for (int bit = 0; bit < stones.length; bit++) {
            boolean isSet = (solution & (1 << bit)) > 0;
            if (isSet) {
                sumLeft += stones[bit];
                left.add(stones[bit]);
            } else {
                sumRight += stones[bit];
                right.add(stones[bit]);
            }
        }
        System.out.println("left: " + Arrays.toString(left.toArray()));
        System.out.println("right: " + Arrays.toString(right.toArray()));
        return Math.abs(sumRight - sumLeft);
    }
}
英文:

I would use bit combination to divide the stones into two groups and sum up every possible combination, comparing them to choose the one with lowest difference:

import java.util.Arrays;
import java.util.LinkedList;

public class Pile {
	/**
	 * return solution as int, if bit is set use the &quot;bitNumber&quot; as index for stone
	 * for one side, otherwise for the other
	 **/
	private static int divide(int[] stones) {
		int solution = -1;
		long minDifference = Long.MAX_VALUE;
		for (int n = 0; n &lt; (1 &lt;&lt; (stones.length + 1)); n++) {
			long sumLeft = 0;
			long sumRight = 0;
			for (int bit = 0; bit &lt; stones.length; bit++) {
				boolean isSet = (n &amp; (1 &lt;&lt; bit)) &gt; 0;
				if (isSet) {
					sumLeft += stones[bit];
				} else {
					sumRight += stones[bit];
				}

			}
			long diff = Math.abs(sumLeft - sumRight);
			if (diff &lt; minDifference) {
				solution = n;
				minDifference = diff;
			}

		}
		return solution;
	}

	public static long minDiff(int[] stones) {
		int solution = divide(stones);
		long sumLeft = 0;
		long sumRight = 0;

		LinkedList&lt;Integer&gt; left = new LinkedList&lt;&gt;();
		LinkedList&lt;Integer&gt; right = new LinkedList&lt;&gt;();
		for (int bit = 0; bit &lt; stones.length; bit++) {
			boolean isSet = (solution &amp; (1 &lt;&lt; bit)) &gt; 0;
			if (isSet) {
				sumLeft += stones[bit];
				left.add(stones[bit]);
			} else {
				sumRight += stones[bit];
				right.add(stones[bit]);
			}
		}
		System.out.println(&quot;left: &quot; + Arrays.toString(left.toArray()));
		System.out.println(&quot;right: &quot; + Arrays.toString(right.toArray()));
		return Math.abs(sumRight - sumLeft);
	}

}

huangapple
  • 本文由 发表于 2020年3月15日 19:19:08
  • 转载请务必保留本文链接:https://java.coder-hub.com/60692290.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定