如何交换两个等长整形数组使其数组和的差最小(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语言实现
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 100
int A[N];
int B[N];
//随机初始化一个数组
void init(int a[], int n)
{
int i;
for(i = ; i < n; ++i)
a[i] = rand() % N;
}
//输出数组
void print(int a[], int n)
{
int i;
for(i = ; i < n; ++i)
printf("%d ", a[i]);
printf("\n--------------------------------------------\n");
} //求数组和
int sum(int a[], int n)
{
int i, sum = ;
for(i = ; i < n; ++i)
sum += a[i];
return sum;
}
//交换整数
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
//n1,n2为数组A和B中实际初始化的元素个数
int solve(int n1, int n2)
{
int i, j; //循环迭代变量
int x, y; //用于保存可交换数字对的索引
int maxSum, minSum; //分别用于保存两个数组的数字之和
int diff; //diff = sum1 - sum2
int maxdiff; // 2 * (A[x] - B[y])
int flag; //标记是否找到可交换的数字对
int temp;
int *pMax; //指向数字总和较大的数组
int *pMin; //指向数字总和较小的数组 //随机初始化数组
init(A, n1);
init(B, n2);
print(A, n1);
print(B, n2);
//求数组中数字之和
maxSum = sum(A, n1);
minSum = sum(B, n2); if(maxSum == minSum)
{
printf("There is no need to swap!\n");
return ;
} //令pMax和pMin分别指向数字总和大的数组以及总和小的数组
pMax = A;
pMin = B;
if(maxSum < minSum)
{
pMax = B;
pMin = A;
swap(&maxSum, &minSum);
}
//循环交换两个数组中的数字对,在交换的过程中,始终
//保持pMax数组的数字总和大于或者等于pMin数组的数字总和。
//也就是保持diff >= 0
diff = maxSum - minSum;
while()
{
flag = ;
x = y = ;
maxdiff = ;
//寻找能够使diff减小的数字对。
//从趋势上来看,
//减小的幅度越大diff收敛的越快,
//while循环的次数也越少
for(i = ; i < n1; ++i)
{
for(j = ; j < n2; ++j)
{
temp = pMax[i] - pMin[j];
if(temp > && (diff - * temp) >= )
{
if(maxdiff < *temp)
{
maxdiff = * temp;
x = i;
y = j;
flag = ;
}
}
}
}
if(flag) //找到了可以使diff减小的数字对
{
printf("swap, pMax[%d]:%d, pMin[%d]:%d\n", x, pMax[x], y, pMin[y]);
diff -= maxdiff;
swap(pMax + x, pMin + y);
print(A, n1);
print(B, n2);
printf("diff = %d\n", diff); }
else //没有找到可以交换的数字对,终止while循环
{
break;
}
}
return diff; //返回两个数组经交换后的最小差值
} int main(int argc, char **argv)
{ srand(time(NULL));
printf("min difference:%d\n", solve(, ));
// system("pause");
// pause();
return ;
}
4. java实现
import java.util.Arrays; /**
*
* @author Administrator
*
*/
public class TestUtil {
private int[] arrysMin = null; private int[] arrysMax = null; private int matchNum = 0; private boolean hasMatched = false; /**
* 返回数组的所有元素的总和
*
* @param arrays
* 待计算数组
* @return 所有元素的总和值
*/
public int getArraySum(int[] arrays) {
int sum = 0;
if (null != arrays) {
for (int i : arrays) {
sum += i;
}
}
return sum;
} /**
* 返回数组的差值
*
* @param array1
* 集合一
* @param array2
* 集合二
* @return 差值
*/
public int getTowArraysMacth(int[] array1, int[] array2) {
Integer l1 = getArraySum(array1);
Integer l2 = getArraySum(array2); if ((l1 - l2) / 2 > 0) {
arrysMax = array1;
arrysMin = array2;
return (l1 - l2) / 2;
} else {
arrysMax = array2;
arrysMin = array1;
return (l2 - l1) / 2;
}
} private boolean isReturn(int[] arrayMax, int[] arrayMin) {
Integer l1 = getArraySum(arrayMax);
Integer l2 = getArraySum(arrayMin); if ((l1 - l2) > 0) {
return false;
} else {
return true;
}
} public void doMatch() {
// 保证大的数组总和永远是大的,以防递归进入死循环
if (isReturn(arrysMax, arrysMin)) {
return;
}
// 获取元素总和大的与小的差值平均值
int diff = getTowArraysMacth(arrysMax, arrysMin);
// 使用一个大数字初始化最小绝对值,后面做比较
int abs = getArraySum(arrysMax);
int tempElement = 0;
// 最终大数组要交换的下标
int maxIndex = -1;
int minIndex = -1;
if (null != arrysMax && null != arrysMin) {
for (int i = 0; i < arrysMax.length; i++) {
for (int j = 0; j < arrysMin.length; j++) {
int temp = arrysMax[i] - arrysMin[j];
if (temp > 0 && diff > temp) {
// 如果元素差值和元素总和大的与小的差值平均值正好相等,直接交换元素OK
if (Math.abs(diff - temp) == 0) {
tempElement = arrysMin[j];
arrysMin[j] = arrysMax[i];
arrysMax[i] = tempElement;
matchNum++;
hasMatched = true;
return;
} else {
// 否则完全遍历,最终找出元素差值和总和差值平均值差距最小的两元素,
if (abs > Math.abs(diff - temp)) {
abs = Math.abs(diff - temp);
maxIndex = i;
minIndex = j;
}
}
}
}
}
//如果没有找到匹配项,且在已变换的数组中找到了满足条件的变量,则继续递归
if (!hasMatched && (maxIndex != -1 || minIndex != -1)) {
// 交换差距最小的两元素
System.out.printf("第%d次交换, Max[%d]:%d, Min[%d]:%d\n", ++matchNum, maxIndex, arrysMax[maxIndex], minIndex, arrysMin[minIndex]);
tempElement = arrysMin[minIndex];
arrysMin[minIndex] = arrysMax[maxIndex];
arrysMax[maxIndex] = tempElement;
System.out.println("交换后Max数组:" + Arrays.toString(arrysMax));
System.out.println("交换后Min数组:" + Arrays.toString(arrysMin));
System.out.println();
// 递归
doMatch();
}
}
} public int getMatchNum() {
return matchNum;
} /**
* @param args
*/
public static void main(String[] args) {
TestUtil tu = new TestUtil();
int[] a1 = { 11, 2, 4, 6, 47 };
int[] a2 = { 4, 5, 8, 9, 2 };
System.out.println("交换前数组a1:" + Arrays.toString(a1));
System.out.println("交换前数组a2:" + Arrays.toString(a2));
// 进行第一次分出,两元素的总和谁大谁小
tu.getTowArraysMacth(a1, a2);
// 开始进行处理交换
tu.doMatch();
// 打印交换结果
System.out.println("交换次数:" + tu.getMatchNum());
System.out.println("a1数组元素和:" + tu.getArraySum(a1));
System.out.println("a2数组元素和:" + tu.getArraySum(a2));
System.out.println("交换后原数组a1:" + Arrays.toString(a1));
System.out.println("交换后原数组a2:" + Arrays.toString(a2));
}
}
参考链接:http://blog.csdn.net/kittyjie/article/details/4386742
http://www.myexception.cn/program/758365.html (此页面中的java实现是有问题的,本文已对其作出修改)
如何交换两个等长整形数组使其数组和的差最小(C和java实现)的更多相关文章
- 有两个数组a,b,大小都为n,;通过交换a,b中的元素,使sum(a)-sum(b)最小。
有两个数组a,b,大小都为n,数组元素的值任意整形数,无序: 要求:通过交换a,b中的元素,使数组a元素的和与数组b元素的和之间的差最小. 当前数组a和数组b的和之差为 A = sum(a) - ...
- The Super Powers UVA 11752 分析分析 求无符号长整形以内的数满足至少可以用两种不同的次方来表示。比如64 = 2^6 = 8^2; 一个数的1次方不算数。
/** 题目:The Super Powers UVA 11752 链接:https://vjudge.net/contest/154246#problem/Y 题意:求无符号长整形以内的数满足至少可 ...
- C/C++ 中的0长数组(柔性数组)
转自C/C++ 中的0长数组(柔性数组) 在标准C和C++中0长数组如charArray[0]是不允许使用的,因为这从语义逻辑上看,是完全没有意义的.但是,GUN中却允许使用,而且,很多时候,应用在了 ...
- 实现pow(int x, int y),即x的y次方 ; 异或交换两个数;
问题1:实现pow(int x, int y) ,即x的y次方 x的y次方就是有y个x连续乘机,代码如下: #include <stdio.h> #include <stdlib.h ...
- URAL 1297 最长回文子串(后缀数组)
1297. Palindrome Time limit: 1.0 secondMemory limit: 64 MB The “U.S. Robots” HQ has just received a ...
- 编程技巧:使用异或操作符(XOR)交换两数值
异或(exclusive OR)作为4种逻辑操作符之一,相对其他3种(OR/AND/NOT)来说,出场的次数非常少,是因为在日常开发中能用到它的场景本来就不多.对笔者来说,目前接触到场景只有交换两个数 ...
- 面试题-JavaScript交换两个变量的方法
在平时的业务开发或者面试过程中,经常会遇到交换两个变量这种问题,于是,个人总结以下几种交换变量的方法: 1.方案一 使用一个临时变量来交换 2.方案二 使用ES6解构赋值语法来交换 3.方案三利用数 ...
- Java 交换两数的方法
错误示范 1. 直接交换 public class SwapNumbers { // 直接交换 public static void swap(int a, int b) { int temp = a ...
- (持续更新)虚树,KD-Tree,长链剖分,后缀数组,后缀自动机
真的就是讲课两天,吸收一个月呢! \(1.\)虚树 \(2.\)KD-Tree \(3.\)长链剖分 \(4.\)后缀数组 后缀数组 \(5.\)后缀自动机 后缀自动机
随机推荐
- SQL Server 添加链接服务器
背景 在SQL SERVER中,如果我们查询数据库需要关联另外一台数据库中表,在这种情况下我们可以通过添加服务器链接来实现. 案列 方式1.sql server 提供了图形化界面,如下: 右键> ...
- 地图源改变之后mxd文件打开很慢的问题
在使用ArcGIS开发电子地图程序时,有时候需要更换服务器地址,这时打开MXD文件就会非常慢,一直没有找到有效的方法,下面是从网上搜到的方法,还没有验证,下次再碰到这个问题的时候,验证一下: (以下方 ...
- NUnitForms 测试GUI应用程序的优秀工具
著名的NUnit是单元测试的优秀工具,但是要在一个测试方法中启动GUI程序,比如Windows Form界面,这比较难做到.NUnitForms就是为解决这个问题产生的,它是NUnit的一个扩展程序, ...
- ZPL打印中文信息
博客来源:http://www.cnblogs.com/Geton/p/3595312.html 相信各位在实际的项目中,需要开发打条码模块的也会有不少,很多同行肯定也一直觉得斑马打印机很不错,但是Z ...
- 安装Weblogic12.1.3时遇到jre1.7.0_71不是有效的 JDK
解决方案:把fmw_12.1.3.0.0_wls.jar放到%JAVA_HOME%\bin目录下,然后执行java -jar fmw_12.1.3.0.0_wls.jar就能安装了.
- XMPP系列2:如何掌握XMPP协议
michaely 回答于 2012-08-07 08:34 举报我要说的是:1.任何一个协议想学习并熟练掌握,都不是一天两天的事情.2.XMPP协议现在已经有很多成熟的架构和客户端,无需重新造轮子.3 ...
- find the peak value
A peak element is an element that is greater than its neighbors. Given an input array where num[i] ≠ ...
- 10个有关RESTful API良好设计的最佳实践
Web API已经在最近几年变成重要的话题,一个干净的API设计对于后端系统是非常重要的. 通常我们为Web API使用RESTful设计,REST概念分离了API结构和逻辑资源,通过Http方法GE ...
- GitHub 优秀的 Android 开源项目(转)
今天查找资源时看到的一篇文章,总结了很多实用资源,十分感谢原作者分享. 转自:http://blog.csdn.net/shulianghan/article/details/18046021 主要介 ...
- Oracle Essbase入门系列(一)
1. 开篇序 本文是几年前做Hyperion Planning项目时写的,后来陆陆续续有些补充.本来打算将整个EPM写一系列的教程,但HFM写到1/3就没动力了.不过至少Essbase这部分是完整的. ...