背包问题

在有容量限制的情况下装下最大价值的物品:

// 设置n个物体重量和对应的价值
int w[] = {0, 1, 2, 5, 4, 3};
int v[] = {0, 1, 2, 3, 4, 5};
// 背包容量
int m = 10;

结果导论:如果在取 [a1,a2...ak] [ a 1 , a 2 . . . a k ] 时价值最大,那么在限制重量为 mw[ak] m − w [ a k ] 时(也就是还未考虑 ak a k 时)取得最大价值的集合必有 [a1,...ak1] [ a 1 , . . . a k − 1 ]

因此,若以dp表示价值,我们只要得到子问题 dp[k1][mw[ak]] d p [ k − 1 ] [ m − w [ a k ] ] 的最大值,就可以得到 dp[k][m] d p [ k ] [ m ] 即结果集所取得的最大值。

但是我们不知道1-k中的实际数值,于是遍历 i[1,n],j[1,m] i ∈ [ 1 , n ] , j ∈ [ 1 , m ] 来得到 dp[n][m] 的最大值。在遍历过程如何确定dp[i][j] 的值呢?

首先如果在限制重量为 j 的情况下不能装入,那么有 i 和没有 i 是没有区别的, 于是 dp[i][j]=dp[i1][j] d p [ i ] [ j ] = d p [ i − 1 ] [ j ]

其次如果能添加 ,说明限制重量 j>=w[i] j >= w [ i ] ,之前的重量最大为 jw[i] j − w [ i ] , 因此添加后的价值为 dp[i][j]=dp[i1][jw[i]]+v[i] d p [ i ] [ j ] = d p [ i − 1 ] [ j − w [ i ] ] + v [ i ] ,但是比较 dp[i1][jw[i]] d p [ i − 1 ] [ j − w [ i ] ] dp[i1][j] d p [ i − 1 ] [ j ] 显然重量的限制不一样,也就说可能为了装入 i 而放弃了某些物品。于是考虑 i 物品但是装不装入由 dp[i][j]=max(dp[i1][j],dp[i1][jw[i]]+v[i]) d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − w [ i ] ] + v [ i ] ) 决定。

        // 设置重量和对应的价值
        int w[] = {0, 1, 2, 5, 4, 3};
        int v[] = {0, 1, 2, 3, 4, 5};
        // 背包容量
        int m = 10;
        int len = w.length-1;

        // dp表示在考虑前i情况下在重量j时能够实现的最大价值
        // dp[i-1][j-w[i]]表示在还未考虑i的且能装下i的最大值
        int dp[][] = new int[len+1][m+1];

        for(int i=1; i<=len; i++){
            for(int j=1; j<=m; j++){

                // 当能装入i且装入价值更大时,放弃某些物品,腾出w[i]空间装入i
                if (j>=w[i] && (dp[i-1][j-w[i]]+v[i] > dp[i-1][j])) {
                    dp[i][j] = dp[i-1][j-w[i]]+v[i];
                }
                // 否则不装入
                else {
                    dp[i][j] = dp[i-1][j];
                }

            }
        }
        System.out.println(dp[len][m]);

逆推得到装入的物品;

        int j = m;
        for(int i=len; i>0; i--){
            // 判断是否装入了i物品
            if (dp[i][j] > dp[i-1][j]) {
                System.out.println(i);
                j =j-w[i];
            }
        }

另外有一种一维数组的逆推方法但是不能找到装入的物品:
https://blog.csdn.net/hearthougan/article/details/53869671

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值