如何交换两个等长整形数组使其数组和的差最小(C和java实现)
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 }
参考链接:http://blog.csdn.net/kittyjie/article/details/4386742
http://www.myexception.cn/program/758365.html (此页面中的java实现是有问题的,本文已对其作出修改)
作者:beanmoon
出处:http://www.cnblogs.com/beanmoon/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
该文章也同时发布在我的独立博客中-豆月博客。