博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
平衡集合
阅读量:4153 次
发布时间:2019-05-25

本文共 2908 字,大约阅读时间需要 9 分钟。

http://blog.csdn.net/v_JULY_v/article/details/6126444的第32题

有两个序列a,b,大小都为n,序列元素的值任意整数,无序;

要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。
例如:
var a=[100,99,98,1,2, 3];
var b=[1, 2, 3, 4,5,40];

上面的两种算法应该都有点问题。

http://blog.csdn.net/cwqbuptcwqbupt/article/details/7521733

因为一次只允许交换一对元素,这对于一次需要交换两个元素的数组而言将出错,考虑如下情况:

A = { 5,5,9,10 };
B = { 4,7,7,13 };
A的和为29,B为31。当把A中的5,5与B中的4,7交换后,A与B的和都为30,差为0.但上述算法一将检测不到这种交换!因此输出结果是原数组。

可以用一种搜索算法

http://blog.csdn.net/ljsspace/article/details/6434621#

有两个序列a,b,大小都为n,序列元素的值任意整数,无序;

要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。
例如:  
var a=[100,99,98,1,2, 3];
var b=[1, 2, 3, 4,5,40];

 

分析:

通过交换的方式,最终的状态是在保证两个序列中元素个数相同的条件下,任何一个元素都可以位于两个序列中的任何一个。这样问题可以转化为:在一个长度为2*n的整数序列中,如何将元素个数分成两个子集,记每个子集的元素之和分别为S1和S2,使得|S1-S2|最小。显然这是一个最优化问题,如果用brute-force方法,组合数是C(2n,n)=(2n)!/(2*(n!)), 如果n很大这个方法不奏效。

 

这里采用回溯法(backtracking),即前序(preorder)遍历状态空间树(state-space tree)。难点在于剪枝条件的确定,下面说明如何确定剪枝条件:

注意到如果将原序列按从小到大的顺序排好序,每次从较大的元素开始取,可以得到一个这样的规律:设长度为2*n序列的元素总和为Sigma,当前集合元素的和为S,剩下的元素之和为Sigma-S,如果二者满足S>=Sigma-S,即Sigma<=2*S,那么在当前集合中剩下需要添加进来的元素必须从余下的元素中取最小的那些元素,这样才能保证|S1-S2|最小。这是因为如果在下一次任意从余下的元素中取的元素分别为e和f,那么取e后的两个子集差为(S+e) - (Sigma-S-e) = 2S-Sigma +2e,取f后的两个子集差为2S-Sigma +2f,显然如果e>f>0, 则有前者的子集差大于后者的子集差(注意这里假设元素都为非负整数,原序列中有负数的情况参考下面的讨论)。

如果s<sigma/2, 就要递归考虑在加入当前值或者不加入当前值的情况

 

如果输入序列中有负整数,可以通过平移操作转化为非负,因为每个数都平移了,它们的差值保值不变。如果不平移,结果不一定正确,比如:输入的2*n序列为:-10,5,3,20,25,50,平衡的对半子集应该为[-10,5,50]和[3,20,25],差值的绝对值为3。在下面的实现中,如果不考虑平移,得到的错误结果却是[-10,3,50]和[5,20,25],差值的绝对值为7。

 

另外在状态空间树只需要考虑根节点的左枝子树,因为原问题考虑的是对半子集。

import java.util.Arrays;import java.util.Stack;/** *  * @author ljs  * 2011-05-20 * 平衡集合问题 * */public class BalancedSet {	//the offset to eliminate negative integers	int OFFSET;	int[] A;	//the total value of the two sets	int sigma;	//the number of elements in each set	int N;	//positive value	int minDiff=Integer.MAX_VALUE;	Stack
tracer = new Stack
(); Stack
bestDiffStack = new Stack
(); public BalancedSet(int[] A) throws Exception{ this.A = A; this.init(); } private void init() throws Exception{ if(A.length % 2 != 0) throw new Exception(); N = A.length / 2; //sort A in ascending order Arrays.sort(A); //offset if possible if(A[0]<0){ OFFSET = -A[0]; for(int i=0;i
=0;){ if(A[i]==bestDiffStack.get(j)){ i++; j--; }else if(A[i] < bestDiffStack.get(j)){ P[p++] = A[i++]; }//else: impossible case } if(i
=0;j--){ bestDiffStack.push(A[j]); } }//else: just throw away this combination }else{ if(i>=1){ //traverse the next subtrees in the state-space tree check(i-1,sum,count,true); check(i-1,sum,count,false); }//else: the check is invalid } } if(include) //backtracking tracer.pop(); } public static void main(String[] args) throws Exception { int A[] = {3,5,-10,20,25,50}; //int A[] = {3,5,10,20,25,50}; //int A[] = {100,99,98,1,2,3,1,2,3,4,5,40}; BalancedSet bs = new BalancedSet(A); bs.solve(A); }}

你可能感兴趣的文章
文件隐藏
查看>>
两个linux内核rootkit--之二:adore-ng
查看>>
两个linux内核rootkit--之一:enyelkm
查看>>
关于linux栈的一个深层次的问题
查看>>
rootkit related
查看>>
配置文件的重要性------轻化操作
查看>>
又是缓存惹的祸!!!
查看>>
为什么要实现程序指令和程序数据的分离?
查看>>
我对C++ string和length方法的一个长期误解------从protobuf序列化说起(没处理好会引起数据丢失、反序列化失败哦!)
查看>>
一起来看看protobuf中容易引起bug的一个细节
查看>>
无protobuf协议情况下的反序列化------貌似无解, 其实有解!
查看>>
make -n(仅列出命令, 但不会执行)用于调试makefile
查看>>
makefile中“-“符号的使用
查看>>
go语言如何从终端逐行读取数据?------用bufio包
查看>>
go的值类型和引用类型------重要的概念
查看>>
求二叉树中结点的最大值(所有结点的值都是正整数)
查看>>
用go的flag包来解析命令行参数
查看>>
来玩下go的http get
查看>>
队列和栈的本质区别
查看>>
matlab中inline的用法
查看>>