如何交换两个等长整形数组使其数组和的差最小(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.\)后缀自动机 后缀自动机
随机推荐
- MySQL完整性语言
文章为作者原创,未经许可,禁止转载. -Sun Yat-sen University 冯兴伟 实验3:完整性语言 完整性语言实验包含3个实验项目,其中2个必修项目,1个选修项目.该实验的各个实验 ...
- 代码大全 MSIL语言程序设计
.NET平台的编译器会将高级语言(C#,VB.NET,F#)编译成MSIL(微软中间语言)格式.熟悉MSIL语言,可以读懂一些加密程序混淆过的算法,这些算法几乎不能还原成高级语言,但是可以还原成MSI ...
- C#WebBrowser控件使用教程与技巧收集--苏飞收集
C#WebBrowser控件使用教程与技巧收集--苏飞收集 先来看看常用的方法 Navigate(string urlString):浏览urlString表示的网址 Navigate(System. ...
- 转:最简单的基于 DirectShow 的视频播放器
50行代码实现的一个最简单的基于 DirectShow 的视频播放器 本文介绍一个最简单的基于 DirectShow 的视频播放器.该播放器对于初学者来说是十分有用的,它包含了使用 DirectSho ...
- SourceTree - 正在检查源... When cloning a repository, "Checking Source" spins forever
I am trying to clone a repository, my OpenSSH is set up correctly and I can do everything fine in Gi ...
- centos7防火墙那些事
转发设置 firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080 firewall-cmd --perma ...
- ubuntu 16.04 U盘多媒体不自动弹出
如下图: 设置 --> 详细信息 --> 可移动媒体 来自为知笔记(Wiz)
- Build Slic3r on Windows // 如何在Windows上编译Slic3r
下载Strawberry Perl 5.22 64bit绿色版,解压缩到某个地方,比如C盘根目录,比如 C:\strawbrry-perl-5.22.2.1-64bit-portable 下载Boos ...
- Scala 深入浅出实战经典 第39讲:ListBuffer、ArrayBuffer、Queue、Stack操作代码实战
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...
- selenium 3.0发布
记得3年前selenium core team就放出风声selenium3.0将在某个圣诞节发布,然而大家等了3年,就在所有人都不再关注selenium进度的时候,selenium3.0 beta1悄 ...