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 }