今天看到一个题目:
有两个数组a,b,大小都为n,数组元素的值任意,无序;
要求:通过交换a,b中的元素,使数组a元素的和与数组b元素的和之间的差最小
没想到很好的方法,上网搜了下,看到这样一个思路,感觉很不错,如下
当前数组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'| = |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为止。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/kittyjie/archive/2009/07/28/4386742.aspx
根据这个思路,用C++实现了一下,代码如下(递归):
void FindMin(int a[], int b[], int n, int &sum)
{
int i;
int j;
int x=0,n1,n2;
bool flag=false;
for (i=0;i<n;i++)
for (j=0;j<n;j++)
{
int tmp=a[i]-b[j];
if ((sum>0 && tmp>0 && tmp<sum)||(sum<0 && tmp<0 && tmp>sum))
{
if(abs(tmp-sum/2)>abs(x-sum/2))
{
x=tmp;
n1=i;
n2=j;
flag=true;
}
}
}
if (flag)
{
sum-=2*x;
a[n1]^=b[n2];
b[n2]^=a[n1];
a[n1]^=b[n2];
FindMin(a,b,n,sum);
}
}
void main()
{
int a[3]={198,1,1};
int b[3]={90,90,30};
int sum=0;
for (int i=0;i<3;i++)
{
sum+=a[i]-b[i];
}
FindMin(a,b,3,sum);
cout<<abs(sum)<<endl;
}