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