mmx_craft 发表于 2006-11-14 15:18

以下思路如何, 先把a,b合成一个2n的数组C, 然后按照如下规则选取。
1. a = c , b = c
2. 按照suma>sumb, 取剩下的最大的给b,最小的给a; 反之最大的给a,最小的给b。

程序如下:

#include <stdio.h>
#include <stdlib.h>

static int
intcompare(const void *p1, const void *p2)
{
    int i = *((int *)p1);
    int j = *((int *)p2);

    if (i > j)
      return (1);
    if (i < j)
      return (-1);
    return (0);
}

int change(int *a, int *b, int size)
{
    int *c = (int*) malloc(2*size);
    int pos_a,pos_b,pos_c,pos_cend;
    int sum_a,sum_b;
    int i;

    memcpy(c, a, sizeof(int)*size);
    memcpy(c+size, b, sizeof(int)*size);
   
    qsort((void *)c, 2*size, sizeof (int), intcompare);

    pos_a=pos_b=pos_c=0;
    pos_cend = 2*size-1;
    a = c;
    b = c;
    sum_a = a;
    sum_b = b;
   
    while (pos_cend > pos_c ) {
      if (sum_a > sum_b) {
            a = c;
            b = c;
            sum_a += a;
            sum_b += b;
      } else {
            a = c;
            b = c;
            sum_a += a;
            sum_b += b;
      }
    }
}

int main (int argc, char **argv)
{
    int a= {1, 2, 3,4,5,6};
    int b= {7, 8, 9,10,11,12};
    int i;
   
    change(a,b,6);


    printf("a=");
    for(i=0; i <6; i++)
      printf("%d,",a);
    printf("\n");
    printf("b=");
    for(i=0; i< 6; i++)
      printf("%d,", b);
    printf("\n");
}

xmyth 发表于 2006-11-14 17:04

当前数组a和数组b的和之差为
A = sum(a) - sum(b)

a的第i个元素和b的第j个元素交换后,a和b的和之差为
A' = sum(a) - a + b - (sum(b) - b + a)
       = sum(a) - sum(b) - 2 (a - b)
       = A - 2 (a - b)
设x = a - b

|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为止。

[ 本帖最后由 xmyth 于 2006-11-14 20:45 编辑 ]

flw2 发表于 2006-11-14 17:42

xmyth 快四年一贴啊.

你说的我看懂了

cuicp 发表于 2006-11-14 19:42

原帖由 xmyth 于 2006-11-14 17:04 发表
当前数组a和数组b的和之差为
A = sum(a) - sum(b)

a的第i个元素和b的第j个元素交换后,a和b的和之差为
A' = sum(a) - a + b - (sum(b) - b + a)
       = sum(a) - sum(b) - 2 (a - b) ...


想法很好呀,又有数学证明,但是什么时候会
找不到(0,A)之间的x呢?
比如:
1,2,3,4,5,12
最佳方案好像是:
4,5,3
1,2,12

那么0<(3-2)<(15-12)

好像不行呀?
是不是要改进一下结束的条件?
我考虑的对不对?
高手看看!
谢谢!!

xmyth 发表于 2006-11-14 20:44

这个结束条件应该是没有问题的

在你说的这个例子里,已经满足了我所提出的结束条件

前面我假设了A>0 (如果A不大于0,对换一下就好了)

所以这里a = {1,2,12}; b = {4,5,3};

x=a-b

所有的x应该都不能满足(0, 3)的条件了,所以满足结束条件了。

exir 发表于 2006-11-14 20:52

真的只有八分钟吗?字都打不完啊。

flw2 发表于 2006-11-14 20:58

原帖由 xmyth 于 2006-11-14 20:44 发表
这个结束条件应该是没有问题的

在你说的这个例子里,已经满足了我所提出的结束条件

前面我假设了A>0 (如果A不大于0,对换一下就好了)

所以这里a = {1,2,12}; b = {4,5,3};

x=a-b

所有的x ...

我一眼就感觉你那公式很可能是对的
这种类似的题很多的
比如100个红包,7个人分,怎么分才最均匀,是不是更头疼?

flw2 发表于 2006-11-14 21:08

我也来出个题

:lol:

greensky_34 发表于 2006-11-14 21:18

用二重循环把a中的每个元素和b中的每个元素逐个“尝试交换”,
如果是差值变小就维持交换,否则就交换回去,也就是不交换,
每有一次交换,“尝试交换”的二重循环就重新从头开始,
直到没有任何交换使差值变小为止。
这种方法不是很简洁,但的确是可行的。
前边贴出代码的算法,有过一次交换后,循环没有从头开始,所以是错误的。例如a和b交换后,b中的元素改变了,不能再保证a和b[ i ]交换不会使差值变小,这时循环必须从头开始。

#include <stdio.h>
#include <math.h>

#define N 10

void ary_init(int a[], int b[]){ //初始化数组
    int i;
    for (i=0; i<N; i++){
      a = rand()%10000;
    }
    for (i=0; i<N; i++){
      b = rand()%100;
    }
    return;
}
long sum(int a[]){ //数组求和
    int i;
    int s;
    for (s=0,i=0; i<N; i++){
      s += a;
    }
    return s;
}

void change(int *a, int *b){ //数组元素交换
    int tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
    return;
}

void prt_ary(int a[], int b[]){ //列印两数组元素及各自元素和
    int i;
    for(i=0; i<N; i++){
      printf("%5d   ", a);
    }
    printf("\n");
    for(i=0; i<N; i++){
      printf("%5d   ", b);
    }
    printf("\n");
    printf("%d\n", sum(a));
    printf("%d\n", sum(b));
}
int slove(int a[], int b[]){
    int i;
    int j;
    int is_swap = 0;//是否修交换过数据标志
    int ab_diff;

    ab_diff = abs(sum(a)-sum(b));
    for (i=0; i<N; i++){
      for (j=0; j<N; j++){
            change(a+i, b+j);
            if (abs(sum(a)-sum(b)) < ab_diff){//判断是否需要交换元素
                is_swap = 1; //置位交换标志
                ab_diff = abs(sum(a)-sum(b)); //更新差值
            }
            else{
                change(b+j, a+i);//不交换

            }
      }
    }
    return is_swap;
}
int main(){

    int a;
    int b;

    ary_init(a,b);//数组元素初始化
    prt_ary(a, b);//列印当前数组状态
    while (slove(a, b));//处理数组
    prt_ary(a, b);//列印交换后数组状态
    getch();
}


[ 本帖最后由 greensky_34 于 2006-11-14 21:20 编辑 ]

cuicp 发表于 2006-11-14 23:14

原帖由 xmyth 于 2006-11-14 20:44 发表
这个结束条件应该是没有问题的

在你说的这个例子里,已经满足了我所提出的结束条件

前面我假设了A>0 (如果A不大于0,对换一下就好了)

所以这里a = {1,2,12}; b = {4,5,3};

x=a-b

所有的x ...

哦,明白了,如果(sum(a) - sum(b)) > (a -b) > 0的话,也就是数组a的和比数组b的和大,
而数组b中有比数组a中小的元素,如果把a和b交换,就可以把数组a和数组b的差缩小,又因为
(sum(a) - sum(b)) > (a -b) ,所以交换后不会出现sum(a) < sum(b)的情况。这样循环调整后,
当整体上sum(a)> sum(b))时,局部上(数组元素)无法交换了,也就是无法找到符合条件的x了。

呵呵,本人天生比较笨,要想一想才能明白!

谢谢xmyth的解释!
页: 1 2 3 4 5 6 7 [8] 9 10 11 12 13 14 15 16 17
查看完整版本: 华为面试题(8分钟写出代码)