SOJ-2111(小强请吃饭)(0-1背包问题,动态规划)

  

0-1背包问题

1.最优子结构性质(用剪贴思想证明)

y1,y2,… yn如果是一个最优解,因若不然,z2,z3,…zn是上述子问题的最优解,从而y1,z2,z3,…zn是0-1背包问题的最优解,这与y1,y2,… yn是最优解矛盾

2.递归关系

m(i,j)=max{m[i+1][j],m(i+1,j-wi)+vi} j>=wi

m[i][j]=m[i+1][j] 0<=j<wi

 

m[n][j]=vn j>=wn

m[n][j]=0 0<=j<wn

算法代码如下,m[1][c]就是最优解

【回溯得到最优解】:如果m[1][c]=m[2][c],则x1=0,否则x1=1.

当x1=0时,m[2][c]继续构造最优解,当x1=1时,m[2][c-wi]继续构造最优解

#define N 10000 + 1
template<class type>
type knapsack(int n, type c, type v[N], type w[N], type m[N][N], int x[N])
{
    int jmax = MY_MIN(w[n] - 1, c);
    int i, j;
    for (j = 0; j <= jmax; ++j) m[n][j] = 0;
    for (j = w[n]; j <= c; ++j) m[n][j] = v[n];
    for (i = n - 1; i > 1; --i) {
        jmax = MY_MIN(w[i] - 1, c);
        for (j = 0; j <= jmax; ++j) m[i][j] = m[i + 1][j];
        for (j = w[i]; j <= c; ++j) m[i][j] = MY_MAX(m[i + 1][j], m[i + 1][j - w[i]] + v[i]);
    }
    m[1][c] = m[2][c];
    if (c >= w[1]) m[1][c] = MY_MAX(m[1][c], m[2][c - w[1]] + v[1]);
    traceback(n, c, w, v, m, x);
    return m[1][c];
}
template<class type>
void traceback(int n, type c, type w[], type v[], type m[N][N], int x[])
{
    int i;
    for (i = 1; i < n; ++i) {
        if (m[i][c] == m[i + 1][c]) x[i] = 0;
        else {
            x[i] = 1;
            c -= w[i];
        }
        //printf("%d ", x[i]);
    }
    x[n] = m[n][c] ? 1 : 0;
    //printf("%d\n", x[n]);
}

上述算法出了需要n*n的空间外,时间效率需要O(nc),而且要求wi是整数。当背包容量c很大时,需要的时间很多,当c>2^n时,需要O(n2^n)

事实上,计算m(i,j)的递归式在变量j是连续变量,即背包容量为实数时仍成立,可以采用以下方法克服这两个缺点:

n=5,    c=10,    w={2, 2, 6, 5, 4},    v={6, 3, 5, 4, 6}

由计算m(i,j)的递归式,当i=5时

m(5,j)=6(j>=4)

m(5,j)=0(0<=j<4)
该函数是一个阶梯状函数,所以只需要保存跳跃点。

p[6]={(0, 0)}

q[6] = p[6] + (w5, v5)={(4, 6)}

p[5] = {(0,0), (4,6)}(p[6]并上q[6])

q[5]=p[5]+(w4, v4)={(5,4), (9,10)}

p[4]={(0,0), (4,6), (5,4), (9,10)}但是需要去掉受控跳跃点(5, 4),所以结果为p[4]={(0,0), (4,6), (9,10)}

……

p[1]的最后那个跳跃点(8, 15)就是最优解,改进后的代码如下(通过了SOJ-2111评测):

/******************************************************************************************************
 ** Copyright (C) 2011.07.01-2013.07.01
 ** Author: famousDT <[email protected]>
 ** Edit date: 2011-10-31
******************************************************************************************************/
#include <stdio.h>
#include <stdlib.h>//abs,atof(string to float),atoi,atol,atoll
#include <math.h>//atan,acos,asin,atan2(a,b)(a/b atan),ceil,floor,cos,exp(x)(e^x),fabs,log(for E),log10
#include <vector>
#include <queue>
#include <map>
#include <time.h>
#include <set>
#include <list>
#include <stack> 
#include <string>
#include <iostream>
#include <assert.h>
#include <string.h>//memcpy(to,from,count
#include <ctype.h>//character process:isalpha,isdigit,islower,tolower,isblank,iscntrl,isprll
#include <algorithm>
using namespace std;

typedef long long ll;

#define MY_PI acos(-1)
#define MY_MAX(a, b) ((a) > (b) ? (a) : (b))
#define MY_MIN(a, b) ((a) < (b) ? (a) : (b))
#define MY_MALLOC(n, type) ((type *)malloc((n) * sizeof(type)))
#define MY_ABS(a) (((a) >= 0) ? (a) : (-(a)))
#define MY_INT_MAX 0x7fffffff

/*==========================================================*\
| 0-1背包
\*==========================================================*/
#define N 10000 + 1
template<class type>
type knapsack(int n, type c, type v[], type w[], type p[N][2], int x[])
{
    int head[N + 2];
    head[n + 1] = 0;
    p[0][0] = p[0][1] = 0;
    int left = 0, right = 0;
    int next = 1;
    head[n] = 1;
    int i, j, k;
    for (i = n; i >= 1; --i) {
        k = left;
        for (j = left; j <= right; ++j) {
            if (p[j][0] + w[i]> c) break;
            type y = p[j][0] + w[i];
            type m = p[j][1] + v[i];
            while (k <= right && p[k][0] < y) {
                p[next][0] = p[k][0];
                p[next++][1] = p[k++][1];
            }
            if (k <= right && p[k][0] == y) {
                if (p[k][1] > m)
                    m = p[k][1];
                ++k;
            }
            if (m > p[next - 1][1]) {
                p[next][0] = y;
                p[next++][1] = m;
            }
            while (k <= right && p[k][1] <= p[next - 1][1]) ++k;
        }
        while (k <= right) {
            p[next][0] = p[k][0];
            p[next++][1] = p[k++][1];
        }
        left = right + 1;
        right = next - 1;
        head[i - 1] = next;
    }
    traceback(n, w, v, p, head, x);
    return p[next - 1][1];
}
template<class type>
void traceback(int n, type w[], type v[], type p[N][2], int *head, int x[])
{
    type j = p[head[0] - 1][0];
    type m = p[head[0] - 1][1];
    int i, k;
    for (i = 1; i <= n; ++i) {
        x[i] = 0;
        for (k = head[i + 1]; k <= head[i] - 1; ++k) {
            if (p[k][0] + w[i] == j && p[k][1] + v[i] == m) {
                x[i] = 1;
                j = p[k][0];
                m = p[k][1];
                break;
            }
        }
        //printf("%d ", x[i]);
    }
    //printf("\n");
}
int main()
{
    int n, c, v[N], w[N], p[N][2], x[N];
    while (scanf("%d%d", &c, &n) == 2) {
        int i;
        for (i = 1; i <= n; ++i) {
            scanf("%d", &w[i]);
        }
        printf("%d\n", knapsack<int>(n, c, w, w, p, x));
    }
    return 0;
}


 

你可能感兴趣的:(c,算法,Date,String,float,n2)