0 0

问一个算法的问题10

现在给出10个数[a,b,c,d,e,f,g,h,i,j]
目的:把它们分成2组,每组相加后的和最接近

比如 acdeg:bfhij和分别为55和57 而 abcde:fghij和分别为43和77


那么acdeg:bfhij 就是我们要找的


我想了半天好像只能用穷举啊

问题补充:我给大家列一组数据 【1,10,11,12,13,14,15,16,17,18】 总和为【127】

一下是最接近的分组
【13,10,11,12,18】和为【64】 以及 【1,14,15,16,17】和为【63】
这样分组应该是两组之和最接近的了

问题补充:我的想法是:
先把所有的组合全部穷举出来,记录两分分组运算结果以及运算结果是由哪些数相加而来
然后将运算结果排序,这样就可以拿到想要的分组
我知道的算法不多,只知道这种情况应该要穷举

问题补充:可能是我没说明白
这个算法里面数据就是固定的10条,并且把它分成两组,每组5个数,两组和最接近
2013年10月22日 11:32

14个答案 按时间排序 按投票排序

0 0

采纳的答案

最终版
将约束条件增加
path.size()==5 即可

package ansj_test.ansj_test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 一个数组中倒找使得两个数组平均化的解
 * 
 * @author ansj
 * 
 */
public class java {
    // 需要计算的数组.ps已经排好序了
    private static int[] arr = new int[] { 1, 1, 2, 2, 3, 3, 4, 4, 5, 9999 };
    // 物品状态0,未放入背包.1放入
    private static int[] status = new int[arr.length];
    // 中位数
    private static int mid = 0;
    // 记录背包中的总数
    static int num = 0;

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Arrays.sort(arr);
        // 找到平均数
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        mid = sum / 2;

        // 构造一个集合使之小于等于mid,此时转化为背包问题
        for (int i = 0; i < arr.length; i++) {
            // 将array[i] 放入背包
            findMid(0, i, new ArrayList<Integer>());
        }

        // 一下代码就是为了打印结果可以忽略
        for (Integer index : maxPath) {
            status[index] = 1;
        }

        List<Integer> result1 = new ArrayList<Integer>();
        List<Integer> result2 = new ArrayList<Integer>();

        for (int i = 0; i < status.length; i++) {
            if (status[i] == 0) {
                result1.add(arr[i]);
            } else {
                result2.add(arr[i]);
            }
        }

        System.out.println("mid is " + mid);
        int sum1 = 0;
        for (Integer integer : result1) {
            sum1 += integer;
        }
        int sum2 = 0;
        for (Integer integer : result2) {
            sum2 += integer;
        }
        System.out.println(result1 + " = " + sum1);
        System.out.println(result2 + " = " + sum2);

    }

    private static int minMid = Integer.MAX_VALUE;
    private static List<Integer> maxPath;

    /**
     * 从i开始寻找一种组合使其最接近midmid
     * 
     * @param i
     */
    private static void findMid(int sum, int i, List<Integer> path) {
        // TODO Auto-generated method stub
        sum += arr[i];
        if (path.size() > 4) {
            return;
        }

        path.add(i);// 记录路径

        if (minMid == 0) { // 找到最优路径直接打印了
            return;
        } else if (path.size() == 5 && minMid > Math.abs(sum - mid)) {
            minMid = Math.abs(sum - mid);
            maxPath = path;
        }

        i++;
        for (int j = i; j < arr.length; j++) {
            findMid(sum, j, new ArrayList<Integer>(path));
        }
    }

}

2013年10月24日 11:22
0 0

这次好了。。。。不好意思。。刚才糊涂了。。看来自己本身的思路还是不够开阔

package ansj_test.ansj_test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 一个数组中倒找使得两个数组平均化的解
 * 
 * @author ansj
 * 
 */
public class java {
    // 需要计算的数组.ps已经排好序了
    private static int[] arr = new int[] { 1, 1, 2, 2, 3, 3, 4, 4, 9999, 9999 };
    // 物品状态0,未放入背包.1放入
    private static int[] status = new int[arr.length];
    // 中位数
    private static int mid = 0;
    // 记录背包中的总数
    static int num = 0;

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Arrays.sort(arr);
        // 找到平均数
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        mid = sum / 2;

        // 构造一个集合使之小于等于mid,此时转化为背包问题
        for (int i = 0; i < arr.length; i++) {
            // 将array[i] 放入背包
            findMid(0, i, new ArrayList<Integer>());
        }

        // 一下代码就是为了打印结果可以忽略
        for (Integer index : maxPath) {
            status[index] = 1;
        }

        List<Integer> result1 = new ArrayList<Integer>();
        List<Integer> result2 = new ArrayList<Integer>();

        for (int i = 0; i < status.length; i++) {
            if (status[i] == 0) {
                result1.add(arr[i]);
            } else {
                result2.add(arr[i]);
            }
        }

        System.out.println("mid is " + mid);
        int sum1 = 0;
        for (Integer integer : result1) {
            sum1 += integer;
        }
        int sum2 = 0;
        for (Integer integer : result2) {
            sum2 += integer;
        }
        System.out.println(result1 + " = " + sum1);
        System.out.println(result2 + " = " + sum2);

    }

    private static int minMid = Integer.MAX_VALUE;
    private static List<Integer> maxPath;

    /**
     * 从i开始寻找一种组合使其最接近midmid
     * 
     * @param i
     */
    private static void findMid(int sum, int i, List<Integer> path) {
        // TODO Auto-generated method stub
        sum += arr[i];
        if (path.size() > 4) {
            return;
        }

        path.add(i);// 记录路径

        if (minMid == 0) { // 找到最优路径直接打印了
            return;
        } else if (minMid > Math.abs(sum-mid)) {
            minMid = Math.abs(sum-mid);
            maxPath = path;
        }

        i++;
        for (int j = i; j < arr.length; j++) {
            findMid(sum, j, new ArrayList<Integer>(path));
        }
    }

}

2013年10月24日 10:57
0 0

好吧,楼主你赢了,如果真要各55分组的话,穷举问题也不大,C 10 5 = 252种情况吧,不多。如果一定要用什么算法,那么用我的方法,然后再把不是55分组的组合去掉,剩下的再比较(leftValue-rightValue)绝对值最接近0的那个情况就行了,呼应我之前的回答,我的结果包含了55分组的情况,而且还提供不止一种答案,不错吧~

2013年10月24日 10:36
0 0

保持5,5结构。

将结束条件改为

if (path.size()>4) {
			return;
		}

就可以
package ansj_test.ansj_test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 一个数组中倒找使得两个数组平均化的解
 * 
 * @author ansj
 * 
 */
public class java {
	// 需要计算的数组.ps已经排好序了
	private static int[] arr = new int[] {1,10,11,12,13,14,15,16,17,18 };
	// 物品状态0,未放入背包.1放入
	private static int[] status = new int[arr.length];
	// 中位数
	private static int mid = 0;
	// 记录背包中的总数
	static int num = 0;

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Arrays.sort(arr);
		// 找到平均数
		int sum = 0;
		for (int i = 0; i < arr.length; i++) {
			sum += arr[i];
		}
		mid = sum / 2;

		// 构造一个集合使之小于等于mid,此时转化为背包问题
		for (int i = 0; i < arr.length; i++) {
			// 将array[i] 放入背包
			findMid(0, i, new ArrayList<Integer>());
		}

		// 一下代码就是为了打印结果可以忽略
		for (Integer index : maxPath) {
			status[index] = 1;
		}

		List<Integer> result1 = new ArrayList<Integer>();
		List<Integer> result2 = new ArrayList<Integer>();

		for (int i = 0; i < status.length; i++) {
			if (status[i] == 0) {
				result1.add(arr[i]);
			} else {
				result2.add(arr[i]);
			}
		}

		System.out.println("mid is " + mid);
		int sum1 = 0;
		for (Integer integer : result1) {
			sum1 += integer;
		}
		int sum2 = 0;
		for (Integer integer : result2) {
			sum2 += integer;
		}
		System.out.println(result1 + " = " + sum1);
		System.out.println(result2 + " = " + sum2);

	}

	private static int maxMid = 0;
	private static List<Integer> maxPath;

	/**
	 * 从i开始寻找一种组合使其最接近midmid
	 * 
	 * @param i
	 */
	private static void findMid(int sum, int i, List<Integer> path) {
		// TODO Auto-generated method stub
		sum += arr[i];
		if (path.size()>4) {
			return;
		}

		path.add(i);// 记录路径

		if (maxMid == mid) { // 找到最优路径直接打印了
			return;
		} else if (maxMid < sum) {
			maxMid = sum;
			maxPath = path;
		}

		i++;
		for (int j = i; j < arr.length; j++) {
			findMid(sum, j, new ArrayList<Integer>(path));
		}
	}

}

2013年10月24日 10:34
0 0

用了一个比较笨的方法

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 一个数组中倒找使得两个数组平均化的解
 * 
 * @author ansj
 * 
 */
public class java {
	// 需要计算的数组.ps已经排好序了
	private static int[] arr = new int[] {11,13,23,23,32,99 };
	// 物品状态0,未放入背包.1放入
	private static int[] status = new int[arr.length];
	// 中位数
	private static int mid = 0;
	// 记录背包中的总数
	static int num = 0;

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Arrays.sort(arr);
		// 找到平均数
		int sum = 0;
		for (int i = 0; i < arr.length; i++) {
			sum += arr[i];
		}
		mid = sum / 2;

		// 构造一个集合使之小于等于mid,此时转化为背包问题
		for (int i = 0; i < arr.length; i++) {
			// 将array[i] 放入背包
			findMid(0, i, new ArrayList<Integer>());
		}

		// 一下代码就是为了打印结果可以忽略
		for (Integer index : maxPath) {
			status[index] = 1;
		}

		List<Integer> result1 = new ArrayList<Integer>();
		List<Integer> result2 = new ArrayList<Integer>();

		for (int i = 0; i < status.length; i++) {
			if (status[i] == 0) {
				result1.add(arr[i]);
			} else {
				result2.add(arr[i]);
			}
		}

		System.out.println("mid is " + mid);
		int sum1 = 0;
		for (Integer integer : result1) {
			sum1 += integer;
		}
		int sum2 = 0;
		for (Integer integer : result2) {
			sum2 += integer;
		}
		System.out.println(result1 + " = " + sum1);
		System.out.println(result2 + " = " + sum2);

	}

	private static int maxMid = 0;
	private static List<Integer> maxPath;

	/**
	 * 从i开始寻找一种组合使其最接近midmid
	 * 
	 * @param i
	 */
	private static void findMid(int sum, int i, List<Integer> path) {
		// TODO Auto-generated method stub
		sum += arr[i];
		if (sum > mid) {
			return;
		}

		path.add(i);// 记录路径

		if (maxMid == mid) { // 找到最优路径直接打印了
			return;
		} else if (maxMid < sum) {
			maxMid = sum;
			maxPath = path;
		}

		i++;
		for (int j = i; j < arr.length; j++) {
			findMid(sum, j, new ArrayList<Integer>(path));
		}
	}

}

2013年10月23日 22:59
0 0

这个就是Partition problem,是NP-完全问题,但存在一个伪多项式动态规划算法,时间复杂度是O(Nn),N是所有输入数字的和,n是数字的个数。详见:http://en.wikipedia.org/wiki/Partition_problem

2013年10月23日 16:58
0 0

晚上没事干自己写了写。应该是这个意思。没找到特殊情况。如果你找到了告诉我我在修改

mport java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
 
/**
 * 一个数组中倒找使得两个数组平均化的解
 * @author ansj
 *
 */
public class java {
	// 需要计算的数组.ps已经排好序了
	private static int[] arr = new int[] { 1, 3, 10, 20, 32, 321, 132, 31, 3, 123, 123, 12, 31, 31, 31, 231, 23, 123, 12, 321, 312, 23, 123 };
	// 物品状态0,未放入背包.1放入
	private static int[] status = new int[arr.length];
	// 中位数
	private static int mid = 0;
	// 记录背包中的总数
	static int num = 0;
 
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Arrays.sort(arr);
		// 找到平均数
		int sum = 0;
		for (int i = 0; i < arr.length; i++) {
			sum += arr[i];
		}
		mid = sum / 2;
 
		// 构造一个集合使之小于等于mid,此时转化为背包问题
		for (int i = 0; i < arr.length; i++) {
			// 将array[i] 放入背包
			if (addInPackage(i)) {
				break;
			}
		}
 
		
		//一下代码就是为了打印结果可以忽略
		List<Integer> result1 = new ArrayList<Integer>();
		List<Integer> result2 = new ArrayList<Integer>();
 
		for (int i = 0; i < status.length; i++) {
			if (status[i] == 0) {
				result1.add(arr[i]);
			} else {
				result2.add(arr[i]);
			}
		}
 
		System.out.println("mid is " + mid);
		int sum1 = 0;
		for (Integer integer : result1) {
			sum1 += integer;
		}
		int sum2 = 0;
		for (Integer integer : result2) {
			sum2 += integer;
		}
		System.out.println(result1 + " = " + sum1);
		System.out.println(result2 + " = " + sum2);
 
	}
 
	private static boolean addInPackage(int i) {
		// TODO Auto-generated method stub
		num += arr[i];
		status[i] = 1;
 
		if (num == mid) {// 等于中位数找到最优解直接跳出了哦
			return true;
		} else if (num < mid) {// 小于中位数背包还能放
			return false;
		} else { // 超过背包容量,需要选择性弹出
			int maxJ = 0;
			for (int j = 0; j < status.length; j++) {// 尝试由小到大弹出
				if (status[j] == 0) {// 说明没放入背包那么不考虑
					continue;
				}
				// 如果弹出后满足条件.那么就弹出
				if (num - arr[j] == mid) {
					pop(j);
					return true;
				} else if (num - arr[j] < mid) {
					// 弹出j
					pop(j);
					return false;
				}
				maxJ = j;
			}
			// 如果一路没有解那么弹出最大结束,弹出maxJ
			pop(maxJ);
			return true;
		}
	}
 
	//弹出操作
	private static void pop(int j) {
		// TODO Auto-generated method stub
		num -= arr[j];
		status[j] = 0;
	}
}

2013年10月23日 14:53
0 0

我看了下你的补充,对于个别数与其他数相距很大的情况下我的方法不适用,那么如何改进呢?很简单,只需将排序算法去除,对于list多进行几次collectins.shuffes(list),然后判断(leftValue-rightValue)绝对值最接近0的那个情况就行了,而且这样一来,可以解出多种答案来。

2013年10月23日 06:50
0 0

我只能说.这个是背包问题...动态优化来解决的.貌似不是很容易...

2013年10月22日 21:45
0 0

楼主不好意思。。来晚了,想了下,再仔细看了你的题目,之前好像把你题目的意思理解错了,看下这个答案如何:

package test;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * @author QuarterLiftForJava
 * @version V0.1
 * 只是一种方法一种答案组合
 */
public class Test {
	
	//假设数值范围为1-10
	private static final int number = 10;
	
	private static List<Integer> list  = new ArrayList<Integer>();
	
	private static List<Integer> leftList  = new ArrayList<Integer>();
	
	private static List<Integer> rightList  = new ArrayList<Integer>();
	
	private static int leftValue = 0;
	
	private static int rightValue = 0;

	public static void main(String[] args) {
		//随机生成1-10之间的十个数
		int arry[] = new int[10];
		for(int i=0;i<10;i++){
			arry[i] = new Random().nextInt(number)+1;
		}
		sort(arry);
		System.out.println(list);
		while(list.size()!=0){
			balance(leftValue,rightValue);
		}
		System.out.println(leftList);
		System.out.println(rightList);
	}
	
	public static void balance(int left,int right){
		int x = list.remove(0);
		if(x+left>x+right){
			rightList.add(x);
			rightValue = rightValue+x;
		}else{
			leftList.add(x);
			leftValue = leftValue+x;
		}
	}

	public static void sort(int[] arry){
		for (int i = 0; i < arry.length; i++) {
			for (int j = 0; j < i; j++) {
				if(arry[i]>arry[j]){
					arry[i] = arry[i]^arry[j];
					arry[j] = arry[i]^arry[j];
					arry[i] = arry[i]^arry[j];
				}
			}
		}
		for (int i = 0; i < arry.length; i++) {
			list.add(arry[i]);
		}
	}
}

2013年10月22日 21:29
0 0

我想的是先排序,然后从大到小开始分组。加上楼上代码的想法写了下面代码,楼主看看是不是你想要的。

package com.sx.test;


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

/**
 * @author  ul
 */
public class TestNum{
	
	//假设数值范围为1-100
	private static final int number = 100;
	private static int before = 0;
	private static int after = 0;
	
	public static void main(String[] args) {
		List<Integer> list = new ArrayList<Integer>();
		int arry[] = new int[10];
		for(int i=0;i<arry.length;i++){
			arry[i] = new Random().nextInt(number)+1;
			list.add(arry[i]);
			System.out.print(arry[i]+" ");
		}
		System.out.println();
		Collections.sort(list);
		
		List<Integer> be = new ArrayList();
		List<Integer> af = new ArrayList();
		
		Integer array[] = list.toArray(new Integer[]{});
		for(int i = 9;i>=0;i--){
			if(before>after){
				after+= array[i];
				af.add(array[i]);
			}else{
				before+=array[i];
				be.add(array[i]);
			}
		}
		System.out.println("前部分:"+be+"和:"+getsum(be));
		System.out.println("后部分:"+af+"和:"+getsum(af));
	}
	
	public static int getsum(List<Integer> a){
		int sum = 0;
		for(Integer i : a){
			sum += i;
		}
		return sum;
	}
}

2013年10月22日 17:48
0 0

不好意思,上班时间,不敢多想。。。好像还是不大对,你再看看吧,回去给你好好想想,更正下。

for(int i=0;i<arry.length/2;i++){
			/**初始化*/
			before += arry[beforecount];
			after  += arry[aftercount];
			/**初始化*/
			if((before==after)||(i>=arry.length/2)){
				break;
			}else if(before<=after){
				before += arry[beforecount++];
			}else if(before>=after){
				after  += arry[aftercount--];
			}
			finalResult = i;
		}

2013年10月22日 16:24
0 0

看下是不是你想要的,如果我对你的要求理解错了,请再和我说下,我再编程,代码如下:

package test;

import java.util.Random;

/**
 * @author  QuarterLifeForJava
 * @version V0.1
 */
public class Test{
	
	//假设数值范围为1-10
	private static final int number = 10;
	private static int beforecount = 0;
	private static int aftercount = 10-1;
	private static int before = 0;
	private static int after = 0;
	private static int finalResult = 0;
	
	public static void main(String[] args) {
		int arry[] = new int[10];
		for(int i=0;i<arry.length;i++){
			arry[i] = new Random().nextInt(number)+1;
			System.out.print(arry[i]+" ");
		}
		System.out.println();
		for(int i=0;i<arry.length/2;i++){
			/**初始化*/
			before += arry[beforecount++];
			after  += arry[aftercount--];
			/**初始化*/
			if((before==after)&&(i>=arry.length/2)){
				break;
			}else if(before<after){
				before += arry[beforecount++];
			}else if(before>after){
				after  += arry[aftercount--];
			}
			finalResult = i;
		}
		System.out.println("前部数组");
		for(int i=0;i<finalResult;i++){
			System.out.print(arry[i]+" ");
		}
		System.out.println();
		System.out.println("后部数组");
		for(int i=finalResult;i<arry.length;i++){
			System.out.print(arry[i]+" ");
		}
	}
}

2013年10月22日 16:15
0 0

先排好序之后再从头穷举分组。
合的差异应该是从大到小过了临界值之后会再变大。
取得该临界值就是结果。

运气好的话是能减少一半左右的循环量的(最差情况是最后一个数是一组,前面所有是一组)。
不过多了一次排序。

2013年10月22日 13:27

相关推荐

Global site tag (gtag.js) - Google Analytics