【Lintcode】1382 · High Capacity Backpack

题目地址:

https://www.lintcode.com/problem/1382/description

给定一个背包,容量 s s s,再给定 n n n个物品,各自体积是 c [ i ] c[i] c[i],各自价值是 v [ i ] v[i] v[i]。问在不超过背包容量的情况下,取物品的最大价值是多少。每个物品限取一次。数据中,容量 s s s和物品的体积和价值取值范围是 1 ≤ s , v [ i ] , c [ i ] ≤ 1 0 13 1\le s,v[i],c[i]\le 10^{13} 1s,v[i],c[i]1013,而 1 ≤ n ≤ 31 1\le n\le 31 1n31

直接用动态规划的话时间复杂度是 O ( n s ) O(ns) O(ns),会超时。这题需要用双向DFS来做,参考https://blog.csdn.net/qq_46105170/article/details/115587834。代码如下:

import java.util.ArrayList;
import java.util.List;

public class Solution {
    
    long res;
    
    /**
     * @param s: The capacity of backpack
     * @param v: The value of goods
     * @param c: The capacity of goods
     * @return: The answer
     */
    public long getMaxValue(int s, int[] v, int[] c) {
        // Write your code here
        int n = v.length, k = n >> 1;
        List<long[]> comb1 = new ArrayList<>();
        // 求前半部分物品的所有组合的体积和价值
        dfs1(0, k, 0, 0, v, c, comb1, s);
        // 去重并优化
        comb1.sort((x, y) -> x[0] == y[0] ? Long.compare(x[1], y[1]) : Long.compare(x[0], y[0]));
        int j = 0;
        for (int i = 0; i < comb1.size(); i++) {
            int ii = i;
            while (ii < comb1.size() && comb1.get(ii)[0] == comb1.get(i)[0]) {
                ii++;
            }
    
            long[] item = comb1.get(ii - 1);
            if (j == 0 || item[1] > comb1.get(j - 1)[1]) {
                comb1.set(j++, item);
            }
            
            i = ii - 1;
        }
        
        // 去完重后,只需考虑不重复的前j个组合
        comb1 = comb1.subList(0, j);
        // 枚举后半部分的所有物品的组合,并且用二分求得体积少于s的最大总价值
        dfs2(k, n, 0, 0, v, c, comb1, s);
        return res;
    }
    
    void dfs1(int u, int k, long sv, long sc, int[] v, int[] c, List<long[]> comb1, int s) {
        if (u == k) {
            comb1.add(new long[]{sc, sv});
            return;
        }
        
        dfs1(u + 1, k, sv, sc, v, c, comb1, s);
        if (sc + c[u] <= s) {
            dfs1(u + 1, k, sv + v[u], sc + c[u], v, c, comb1, s);
        }
    }
    
    void dfs2(int u, int n, long sv, long sc, int[] v, int[] c, List<long[]> comb1, int s) {
        if (u == n) {
            int l = 0, r = comb1.size() - 1;
            while (l < r) {
                int mid = l + (r - l + 1 >> 1);
                if (comb1.get(mid)[0] + sc <= s) {
                    l = mid;
                } else {
                    r = mid - 1;
                }
            }
            res = Math.max(res, comb1.get(l)[1] + sv);
            return;
        }
        
        dfs2(u + 1, n, sv, sc, v, c, comb1, s);
        if (sc + c[u] <= s) {
            dfs2(u + 1, n, sv + v[u], sc + c[u], v, c, comb1, s);
        }
    }
}

时间复杂度 O ( n 2 n / 2 ) O(n2^{n/2}) O(n2n/2),空间 O ( 2 n / 2 ) O(2^{n/2}) O(2n/2)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值