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# 中用到了回溯法 但評論中說存在問題 我還沒仔細看