题目地址:
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} 1≤s,v[i],c[i]≤1013,而 1 ≤ n ≤ 31 1\le n\le 31 1≤n≤31。
直接用动态规划的话时间复杂度是 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)。