1. 问题描述:

  有两个数组a,b,大小都为n,数组元素的值任意整形数,无序;
  要求:通过交换a,b中的元素,使[数组a元素的和]与[数组b元素的和]之间的差最小。

2. 求解思路:

    当前数组a和数组b的和之差为
    A = sum(a) - sum(b)
    a的第i个元素和b的第j个元素交换后,a和b的和之差为
    A' = sum(a) - a[i] + b[j] - (sum(b) - b[j] + a[i])
        = sum(a) - sum(b) - 2 (a[i] - b[j])
        = A - 2 (a[i] - b[j])
    设x = a[i] - b[j], 则 |A'| = |A-2x|
    假设A > 0, 
    当x在(0,A)之间时,做这样的交换才能使得交换后的a和b的和之差变小,x越接近A/2效果越好, 如果找不到在(0,A)之间的x,则当前的a和b就是答案。
    所以算法大概如下:
    在a和b中寻找使得x在(0,A)之间并且最接近A/2的i和j,交换相应的i和j元素,重新计算A后,重复前面的步骤直至找不到(0,A)之间的x为止。

3. C语言实现



1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <time.h>
  4 #define N 100
  5 int A[N];
  6 int B[N];
  7 //随机初始化一个数组
  8 void init(int a[], int n)
  9 {
 10   int i;
 11   for(i = 0; i < n; ++i)
 12     a[i] = rand() % N;
 13 }
 14 //输出数组
 15 void print(int a[], int n)
 16 {
 17   int i;
 18   for(i = 0; i < n; ++i)
 19     printf("%d ", a[i]);
 20   printf("\n--------------------------------------------\n");
 21 }
 22  
 23 //求数组和
 24 int sum(int a[], int n)
 25 {
 26   int i, sum = 0;
 27   for(i = 0; i < n; ++i)
 28     sum += a[i]; 
 29   return sum;
 30 }
 31 //交换整数
 32 void swap(int *a, int *b)
 33 {
 34   int temp = *a;
 35   *a = *b;
 36   *b = temp;
 37 }
 38 //n1,n2为数组A和B中实际初始化的元素个数
 39 int solve(int n1, int n2)
 40 {
 41   int i, j; //循环迭代变量
 42   int x, y; //用于保存可交换数字对的索引
 43   int maxSum, minSum; //分别用于保存两个数组的数字之和
 44   int diff; //diff = sum1 - sum2
 45   int maxdiff; // 2 * (A[x] - B[y])
 46   int flag; //标记是否找到可交换的数字对
 47   int temp;
 48   int *pMax; //指向数字总和较大的数组
 49   int *pMin; //指向数字总和较小的数组
 50         
 51   //随机初始化数组
 52   init(A, n1);
 53   init(B, n2);
 54   print(A, n1);
 55   print(B, n2);
 56   //求数组中数字之和
 57   maxSum = sum(A, n1);
 58   minSum = sum(B, n2);
 59         
 60   if(maxSum == minSum)
 61     {
 62       printf("There is no need to swap!\n");
 63       return 0;
 64     }
 65         
 66   //令pMax和pMin分别指向数字总和大的数组以及总和小的数组
 67   pMax = A;
 68   pMin = B;
 69   if(maxSum < minSum)
 70     {
 71       pMax = B;
 72       pMin = A;
 73       swap(&maxSum, &minSum);
 74     }
 75   //循环交换两个数组中的数字对,在交换的过程中,始终
 76   //保持pMax数组的数字总和大于或者等于pMin数组的数字总和。
 77   //也就是保持diff >= 0
 78   diff = maxSum - minSum;
 79   while(1)
 80     {
 81       flag = 0;
 82       x = y = 0;
 83       maxdiff = 0;
 84       //寻找能够使diff减小的数字对。
 85       //从趋势上来看,
 86       //减小的幅度越大diff收敛的越快,
 87       //while循环的次数也越少
 88       for(i = 0; i < n1; ++i)
 89     {
 90       for(j = 0; j < n2; ++j)
 91         {
 92           temp = pMax[i] - pMin[j];
 93           if(temp > 0 && (diff - 2 * temp) >= 0)
 94         {
 95           if(maxdiff < 2 *temp)
 96             {
 97               maxdiff = 2 * temp;
 98               x = i;
 99               y = j;
100               flag = 1;
101             }
102         }
103         }
104     }
105       if(flag) //找到了可以使diff减小的数字对
106     {
107       printf("swap, pMax[%d]:%d, pMin[%d]:%d\n", x, pMax[x], y, pMin[y]);
108       diff -= maxdiff;
109       swap(pMax + x, pMin + y);
110       print(A, n1);
111       print(B, n2);
112       printf("diff = %d\n", diff);
113 
114     }
115       else //没有找到可以交换的数字对,终止while循环
116     {
117       break;
118     }
119     }
120   return diff; //返回两个数组经交换后的最小差值
121 }
122 
123 int main(int argc, char **argv)
124 {
125   
126   srand(time(NULL));
127   printf("min difference:%d\n", solve(5, 5)); 
128   //  system("pause");
129   //  pause();
130   return 0;
131 }



 4. java实现



1 import java.util.Arrays;
  2 
  3 /**
  4  * 
  5  * @author Administrator
  6  * 
  7  */
  8 public class TestUtil {
  9     private int[] arrysMin = null;
 10 
 11     private int[] arrysMax = null;
 12 
 13     private int matchNum = 0;
 14 
 15     private boolean hasMatched = false;
 16 
 17     /**
 18      * 返回数组的所有元素的总和
 19      * 
 20      * @param arrays
 21      *            待计算数组
 22      * @return 所有元素的总和值
 23      */
 24     public int getArraySum(int[] arrays) {
 25         int sum = 0;
 26         if (null != arrays) {
 27             for (int i : arrays) {
 28                 sum += i;
 29             }
 30         }
 31         return sum;
 32     }
 33 
 34     /**
 35      * 返回数组的差值
 36      * 
 37      * @param array1
 38      *            集合一
 39      * @param array2
 40      *            集合二
 41      * @return 差值
 42      */
 43     public int getTowArraysMacth(int[] array1, int[] array2) {
 44         Integer l1 = getArraySum(array1);
 45         Integer l2 = getArraySum(array2);
 46 
 47         if ((l1 - l2) / 2 > 0) {
 48             arrysMax = array1;
 49             arrysMin = array2;
 50             return (l1 - l2) / 2;
 51         } else {
 52             arrysMax = array2;
 53             arrysMin = array1;
 54             return (l2 - l1) / 2;
 55         }
 56     }
 57 
 58     private boolean isReturn(int[] arrayMax, int[] arrayMin) {
 59         Integer l1 = getArraySum(arrayMax);
 60         Integer l2 = getArraySum(arrayMin);
 61 
 62         if ((l1 - l2) > 0) {
 63             return false;
 64         } else {
 65             return true;
 66         }
 67     }
 68 
 69     public void doMatch() {
 70         // 保证大的数组总和永远是大的,以防递归进入死循环
 71         if (isReturn(arrysMax, arrysMin)) {
 72             return;
 73         }
 74         // 获取元素总和大的与小的差值平均值
 75         int diff = getTowArraysMacth(arrysMax, arrysMin);
 76         // 使用一个大数字初始化最小绝对值,后面做比较
 77         int abs = getArraySum(arrysMax);
 78         int tempElement = 0;
 79         // 最终大数组要交换的下标
 80         int maxIndex = -1;
 81         int minIndex = -1;
 82         if (null != arrysMax && null != arrysMin) {
 83             for (int i = 0; i < arrysMax.length; i++) {
 84                 for (int j = 0; j < arrysMin.length; j++) {
 85                     int temp = arrysMax[i] - arrysMin[j];
 86                     if (temp > 0 && diff > temp) {
 87                         // 如果元素差值和元素总和大的与小的差值平均值正好相等,直接交换元素OK
 88                         if (Math.abs(diff - temp) == 0) {
 89                             tempElement = arrysMin[j];
 90                             arrysMin[j] = arrysMax[i];
 91                             arrysMax[i] = tempElement;
 92                             matchNum++;
 93                             hasMatched = true;
 94                             return;
 95                         } else {
 96                             // 否则完全遍历,最终找出元素差值和总和差值平均值差距最小的两元素,
 97                             if (abs > Math.abs(diff - temp)) {
 98                                 abs = Math.abs(diff - temp);
 99                                 maxIndex = i;
100                                 minIndex = j;
101                             }
102                         }
103                     }
104                 }
105             }
106             //如果没有找到匹配项,且在已变换的数组中找到了满足条件的变量,则继续递归
107             if (!hasMatched && (maxIndex != -1 || minIndex != -1)) {
108                 // 交换差距最小的两元素
109                 System.out.printf("第%d次交换, Max[%d]:%d, Min[%d]:%d\n", ++matchNum, maxIndex, arrysMax[maxIndex], minIndex, arrysMin[minIndex]);
110                 tempElement = arrysMin[minIndex];
111                 arrysMin[minIndex] = arrysMax[maxIndex];
112                 arrysMax[maxIndex] = tempElement;
113                 System.out.println("交换后Max数组:" + Arrays.toString(arrysMax));
114                 System.out.println("交换后Min数组:" + Arrays.toString(arrysMin));
115                 System.out.println();
116                 // 递归
117                 doMatch();
118             }
119         }
120     }
121 
122     public int getMatchNum() {
123         return matchNum;
124     }
125 
126     /**
127      * @param args
128      */
129     public static void main(String[] args) {
130         TestUtil tu = new TestUtil();
131         int[] a1 = { 11, 2, 4, 6, 47 };
132         int[] a2 = { 4, 5, 8, 9, 2 };
133         System.out.println("交换前数组a1:" + Arrays.toString(a1));
134         System.out.println("交换前数组a2:" + Arrays.toString(a2));
135         // 进行第一次分出,两元素的总和谁大谁小
136         tu.getTowArraysMacth(a1, a2);
137         // 开始进行处理交换
138         tu.doMatch();
139         // 打印交换结果
140         System.out.println("交换次数:" + tu.getMatchNum());
141         System.out.println("a1数组元素和:" + tu.getArraySum(a1));
142         System.out.println("a2数组元素和:" + tu.getArraySum(a2));
143         System.out.println("交换后原数组a1:" + Arrays.toString(a1));
144         System.out.println("交换后原数组a2:" + Arrays.toString(a2));
145     }
146 }