0-1背包问题

0-1背包问题

给n个重量为w1,w2,w3,...,wn,且价值为v1,v2,v3,...vn的物品和容量为C的背包,每个东西只能选择用一次或者不用(这就是0-1的由来),求这个物品的一个最有价值的子集,使得在满足背包容量的前提下,包内价值最大。

动态规划解决

我们定义dp[i][j]为在有i个物品且背包容量为j的前提下,包内最大的价值;
那么则有
1 不放第i个物品时,dp[i][j]=dp[i-1][j]
2 放第i个物品时,dp[i][j]=dp[i-1][j-w[i]]+v[i]
所以状态转移方程就是dp[i][j]=max(dp[i-1][j],dp[i][j]=dp[i-1][j-w[i]]+v[i])

public class KnapSack01 {
    public static int knapSack(int[] w, int[] v, int C) {
        int size = w.length;
        if (size == 0) {
            return 0;
        }

        int[][] dp = new int[size][C + 1];
        //不放物品时显然价值为0
        for (int i = 0; i <= C; i++) {
            dp[0][i] = 0;
        }
        //背包容量为0时价值也为0
        for(int i=0;i<=size;i++)
            dp[i][0]=0;
        //填充其他行和列
        for (int i = 1; i < size; i++) {
            for (int j = 0; j <= C; j++) {
                dp[i][j] = dp[i - 1][j];
                if (w[i] <= j) {
                    dp[i][j] = Math.max(dp[i][j], v[i] + dp[i - 1][j - w[i]]);
                }
            }
        }
        return dp[size - 1][C];
    }

    public static void main(String[] args) {
        int[] w = {2, 1, 3, 2};
        int[] v = {12, 10, 20, 15};
        System.out.println(knapSack(w, v, 5));
    }
}

根据上述代码,时间复杂度为O(sizeC),这已经是最快的了,无法再简化,但是空间复杂度O(sizeC)可以在简化,这里我们使用了二维数组,因此接下来使用一维数组存储
这里我们定义dp[i]为容量为i时的最大价值,则有推导
dp[i]=dp[i-1] 不放下一个物品
dp[i]=dp[i-w[j]]+v[j] 放下一个物品
所以状态转移方程就是dp[i]=max(dp[i-1],dp[i-w[j]]+v[j]).


public class KnapSack01 {
    public static int knapSack(int[] w, int[] v, int C) {
        int size = w.length;
        if (size == 0) {
            return 0;
        }

        int[] dp = new int[C + 1];
        //初始化所有价值都为0,其实只要保证是一个很小的数就行
        for (int i = 0; i <= C; i++) {
            dp[i] =0;
        }

        for (int i = 1; i < size; i++) {
            for (int j = C; j >= w[i]; j--) {
                dp[j] = Math.max(dp[j], v[i] + dp[j - w[i]]);
            }
        }
        return dp[C];
    }

    public static void main(String[] args) {
        int[] w = {2, 1, 3, 2};
        int[] v = {12, 10, 20, 15};
        System.out.println(knapSack(w, v, 5));
    }
}

这样空间复杂度就是O(C)了。
说这么多,来看真题
![E(0EZK9QZ@RNL3(XCBGPL7.png
这道题可以理解为每张邮票的价值都是1,现在要求一个最佳子集使得邮票的价值最小也就是邮票的数量最小,显然是0-1背包问题,下面是代码

#include 

int main()
{
    int weight[21];
    int f[101];
    int m,n,i,j;
    while(scanf("%d",&m)!=EOF)
    {
        scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%d",&weight[i]);
    f[0]=0;
        //初始化
        for(i=1;i<=m;i++)
            f[i]=1000000;
    for(i=1;i<=n;i++)
    {
        for(j=m;j>=weight[i];j--)
        {
             f[j]=f[j]0&&f[m]<=n)
        printf("%d",f[m]);
        else
            printf("0");
    }
    return 0;
}

恩,今天就到这里,下次有时间看一下其他背包问题,这个0-1背包也要继续练习。
但行好事,莫问前程!

你可能感兴趣的:(0-1背包问题)