0/1揹包問題:python
某種限制下的最優問題。總體最優能夠由部分最優獲得。算法
因爲子問題相交,能夠用動態規劃方法求解。即,利用空間記錄中間計算結果,後續的計算經過簡單的判斷和查表獲得。數組
中間結果的存儲主要是key-value鍵值對。對於內建dictionary支持的python或者有STL庫的C++,解這種問題比較方便,代碼也少。若是以上二者都沒有,好比C,就麻煩點。若是key--(x, y, z, ...)中的x,y,z等都是整數,那麼能夠利用n維數組來存儲中間結果,其index就是key(好比如下代碼對0/1揹包問題的實現)。dom
固然,若是總重量比較大,實際上用數組存儲是不對的,其效率可能遠低於brute force算法。利用dictionary(或者map)來存儲,在0/1揹包問題上,在全部狀況下,都比brute force好(至少不會更差)。spa
/** * knapsack.c -- 0/1 knapsack problem * **/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <unistd.h> #include <time.h> static int N = 0; static int W = 0; static int *p = NULL; static int *w = NULL; static int **T = NULL; static int **record = NULL; static void knapsack(void) { int i=0; int weight=0; for(i=0; i<N; i++) { for (weight=1; weight<=W; weight++) { if (i == 0) { if (w[i] <= weight) { T[i][weight] = p[i]; record[i][weight] = 1; } else { T[i][weight] = 0; record[i][weight] = 0; } continue; } if (w[i] <= weight) { int v1 = T[i-1][weight-w[i]]+p[i]; int v2 = T[i-1][weight]; if (v1 >= v2) { record[i][weight] = 1; T[i][weight] = v1; } else { record[i][weight] = 0; T[i][weight] = v2; } } else { T[i][weight] = T[i-1][weight]; record[i][weight] = 0; } } } } static void backtrace(void) { int i=N-1; int weight=W; printf("selected: "); while(i >= 0 && weight >= 0) { if (record[i][weight] == 1) { printf("%4d", i); weight = weight - w[i]; i--; } else { i--; } } printf("\n"); } static void init(void) { int i = 0; assert(N > 0); assert(W > 0); p = (int *)malloc(sizeof(int) * N); assert(p != NULL); w = (int *)malloc(sizeof(int) * N); assert(w != NULL); for (i=0; i<N; i++) { p[i] = random()%10+1; /* 1~10 */ w[i] = random()%W + 1; /* 1~W */ } T = (int **)malloc(sizeof(int*) * N); assert(T != NULL); for (i=0; i<N; i++) { T[i] = (int*)malloc(sizeof(int) * (W+1)); assert(T[i] != NULL); } record = (int **)malloc(sizeof(int*) * N); assert(record != NULL); for (i=0; i<N; i++) { record[i] = (int*)malloc(sizeof(int) * (W+1)); assert(record[i] != NULL); } for(i=0; i<N; i++) T[i][0] = 0; /* no more available weight */ } static void clean(void) { int i = 0; assert(p != NULL); assert(w != NULL); assert(record != NULL); assert(T != NULL); for (i=0; i<N; i++) free(record[i]); free(record); for (i=0; i<N; i++) free(T[i]); free(T); free(p); free(w); p = NULL; w = NULL; T = NULL; record = NULL; N = 0; W = 0; } static void print_p(void) { int i; printf("p: "); for (i=0; i<N; i++) printf("%4d", p[i]); printf("\n"); } static void print_w(void) { int i; printf("w: "); for(i=0; i<N; i++) printf("%4d", w[i]); printf("\n"); } static void test(int n, int w) { N = n; W = w; init(); printf("N = %d \t W = %d \n", N, W); print_p(); print_w(); knapsack(); printf("MaxTotal = %d \n", T[N-1][W]); backtrace(); clean(); } int main(int argc, char *argv[]) { int i, ret; srand((unsigned int)time(NULL)); printf("================== Test 1 =======================\n"); test(3, 20); printf("================== Test 2 =======================\n"); test(10, 20); printf("================== Test 3 =======================\n"); test(10, 100); return 0; }
以前有道題是2n個整數,分紅n-n兩個集合A和B,求sum(A) - sum(B)的絕對值最小的分法。code
用map(或者dictionary)來存吃key-value,能夠很快解決這道題,如論總的sum多大。遞歸
定義m(i, k, diff) 爲最多到index爲i,還剩k個元素須要歸入(好比n=10,而後已經選取了4個,那麼k=6),最接近但小於等於diff的值。原始call爲m(2n-1, n, totalsum/2).string
因而就有key-value pair爲(i,k,diff) -- m. 而後用相似上面的方法求解。這種狀況下,應該要用遞歸倒着寫。it