用回溯法(backtracking)解决平衡集合问题(一道微软公司面试题)

(原题出自微软公司面试题)问题如下:

有两个序列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, 则有前者的子集差大于后者的子集差(注意这里假设元素都为非负整数,原序列中有负数的情况参考下面的讨论)。

 

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

 

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

 

 

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
非常抱歉,我的回答出现了错误,下面是使用回溯法解决01背包问题的Java实现代码: ```java public class KnapsackProblem { private static int maxWeight = 0; // 最大价值 private static int[] weight; // 物品重量数组 private static int[] value; // 物品价值数组 private static int n; // 物品数量 private static int w; // 背包容量 public static void main(String[] args) { // 初始化数据 weight = new int[]{2, 2, 6, 5, 4}; // 物品重量 value = new int[]{6, 3, 5, 4, 6}; // 物品价值 n = 5; // 物品数量 w = 10; // 背包容量 backtracking(0, 0, 0); System.out.println("最大价值:" + maxWeight); } /** * 回溯法求解01背包问题 * * @param i 当前物品下标 * @param cw 当前物品重量之和 * @param cv 当前物品价值之和 */ private static void backtracking(int i, int cw, int cv) { if (cw == w || i == n) { // 达到边界条件 if (cv > maxWeight) { maxWeight = cv; } return; } // 不选择第i个物品 backtracking(i + 1, cw, cv); // 选择第i个物品 if (cw + weight[i] <= w) { backtracking(i + 1, cw + weight[i], cv + value[i]); } } } ``` 在上面的代码中,我们使用了`weight`数组来存储每个物品的重量,`value`数组来存储每个物品的价值,`n`表示物品的数量,`w`表示背包的容量。`maxWeight`用来存储最大的价值。 在`backtracking`方法中,如果`cw`等于`w`或者`i`等于`n`,表示已经达到了边界条件,根据当前的价值`cv`更新`maxWeight`即可。否则,我们可以选择不选择第`i`个物品,也可以选择第`i`个物品。如果选择第`i`个物品,需要判断当前的重量是否超过了背包的容量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值