【編程題目】有兩個序列 a,b,大小都為 n,序列元素的值任意整數,無序;(需要回頭仔細研究)


32.(數組、規划)
有兩個序列 a,b,大小都為 n,序列元素的值任意整數,無序;
要求:通過交換 a,b 中的元素,使[序列 a 元素的和]與[序列 b 元素的和]之間的差最小。
例如:
var a=[100,99,98,1,2,3];
var b=[1,2,3,4,5,40];

 

首先,目標一定是先找到n個數字,使得數字和比總和的一半小,但是最接近。

思路一:開始看這道題跟之前學的動態規划很像,就想用動態規划來解。但是....做不出來........... 必須要選一半的數字讓我頭都大了。

思路二:接着想寫遞歸,就是把a, b(各n個)中的數字都放在 c 中,選一半的數字n個那就可以分為:

1.選入最后一個數字 第2n - 1個,在剩下的數字中選擇其他 n - 1個數字

2.不選入最后一個數字, 在剩下的數字中選擇 n個數字

這樣,遞歸的思路就有了。

可是在怎么把得到的結果傳出來上面我又犯了難,每個數字都是一層層遞歸選的,怎么傳出來啊?? 好了,到這里卡死了......

void choose(int * c, int len, int N, int sum) //N是要從中挑選幾個數字
{
    static int sumtmax = 0;
    static int sumt = 0;
    static int num = 0;
    if(len < N)
    {
        return;
    }
    if(N == 0)
    {
        if(sumt <= sum/2 && sumt > sumtmax)
        {
            sumtmax = sumt;
        }
        printf("%d : %d  %d\n", ++num, sumt, sumtmax);
        return;
    }

    choose(c, len - 1, N, sum);

    sumt += c[len - 1];
    choose(c, len - 1, N - 1, sum);
    sumt -= c[len - 1];
}

void exchange(int * a, int * b, int len)
{
    int * c = (int *)malloc(2 * len * sizeof(int));
    int * smaller = (int *)malloc(len * sizeof(int));
    int sum = 0;
    for(int i = 0; i < len; i++)
    {
        c[i] = a[i];
        sum += a[i];
    }
    for(int i = 0; i < len; i++)
    {
        c[i + len] = b[i];
        sum += b[i];
    }

    choose(c, len * 2, len, sum);
    
}

 

思路三:和12個人排兩列的思路相同。用二進制 用數字i 從0 - (1 << 2 * n)的范圍變化, 其二進制中的1,就表示數字選入a, 我們遍歷所有的可能找到a中的和最接近總和一半的組合。再另外用一個數字保存最接近時的分布就好了。

不過這樣的寫法一個很不好的限制就是int的位數是有限的,只有64位,也就是說n大於32時我的方法就失效了

/*
32.(數組、規划)
有兩個序列 a,b,大小都為 n,序列元素的值任意整數,無序;
要求:通過交換 a,b 中的元素,使[序列 a 元素的和]與[序列 b 元素的和]之間的差最小。
例如:  
var a=[100,99,98,1,2,3];
var b=[1,2,3,4,5,40];
*/

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

int bit_c(int n) //統計n的二進制表達中有幾個1
{
    int result = 0;
    for(; n; n &= n-1, result++);
    return result;
}

void exchange2(int * a, int * b, int len)
{
    int * c = (int *)malloc(2 * len * sizeof(int));
    int * smaller = (int *)malloc(len * sizeof(int));
    int sum = 0;
    for(int i = 0; i < len; i++)
    {
        c[i] = a[i];
        sum += a[i];
    }
    for(int i = 0; i < len; i++)
    {
        c[i + len] = b[i];
        sum += b[i];
    }

    int maxsumh = 0;
    int mask = 0;
    for(int i = 0; i < (1 << len * 2); i++)
    {
        if(bit_c(i) == len)
        {
            int sumhalf = 0;
            for(int j = 0; j < len * 2; j++)
            {
                if(((i >> j) & 1) == 1)
                {
                    sumhalf += c[j];
                }
            }
            if(sumhalf <= sum / 2 && sumhalf > maxsumh)
            {
                maxsumh = sumhalf;
                mask = i;
            }
        }
    }

    int ai = 0, bi = 0;
    for(int j = 0; j < len * 2; j++)
    {
        if(((mask >> j) & 1) == 1)
        {
            a[ai++] = c[j];
        }
        else
        {
            b[bi++] = c[j];
        }
    }

    printf("最小差值為:%d", sum - 2 * maxsumh);

    free(c);
    
}

int main()
{
    int a[5] = {1,2,3,4,5};
    int b[5] = {30, 40, 50, 60, 23};
    exchange2(a, b, 5);
    return 0;
}

 

網上有各種解法:

http://blog.csdn.net/tianshuai1111/article/details/7479767 中提出了兩種。

但其中一種被http://blog.csdn.net/cwqbuptcwqbupt/article/details/7521733 用反例否決了。

http://blog.csdn.net/ljsspace/article/details/6434621# 中用到了回溯法 但評論中說存在問題 我還沒仔細看


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM