题目
//在 LeetCode 商店中, 有 n 件在售的物品。每件物品都有对应的价格。然而,也有一些大礼包,每个大礼包以优惠的价格捆绑销售一组物品。 // // 给你一个整数数组 price 表示物品价格,其中 price[i] 是第 i 件物品的价格。另有一个整数数组 needs 表示购物清单,其中 needs[ //i] 是需要购买第 i 件物品的数量。 // // 还有一个数组 special 表示大礼包,special[i] 的长度为 n + 1 ,其中 special[i][j] 表示第 i 个大礼包中内含第 j // 件物品的数量,且 special[i][n] (也就是数组中的最后一个整数)为第 i 个大礼包的价格。 // // 返回 确切 满足购物清单所需花费的最低价格,你可以充分利用大礼包的优惠活动。你不能购买超出购物清单指定数量的物品,即使那样会降低整体价格。任意大礼包可无限 //次购买。 // // // // 示例 1: // // //输入:price = [2,5], special = [[3,0,5],[1,2,10]], needs = [3,2] //输出:14 //解释:有 A 和 B 两种物品,价格分别为 ¥2 和 ¥5 。 //大礼包 1 ,你可以以 ¥5 的价格购买 3A 和 0B 。 //大礼包 2 ,你可以以 ¥10 的价格购买 1A 和 2B 。 //需要购买 3 个 A 和 2 个 B , 所以付 ¥10 购买 1A 和 2B(大礼包 2),以及 ¥4 购买 2A 。 // // 示例 2: // // //输入:price = [2,3,4], special = [[1,1,0,4],[2,2,1,9]], needs = [1,2,1] //输出:11 //解释:A ,B ,C 的价格分别为 ¥2 ,¥3 ,¥4 。 //可以用 ¥4 购买 1A 和 1B ,也可以用 ¥9 购买 2A ,2B 和 1C 。 //需要买 1A ,2B 和 1C ,所以付 ¥4 买 1A 和 1B(大礼包 1),以及 ¥3 购买 1B , ¥4 购买 1C 。 //不可以购买超出待购清单的物品,尽管购买大礼包 2 更加便宜。 // // // // 提示: // // // n == price.length // n == needs.length // 1 <= n <= 6 // 0 <= price[i] <= 10 // 0 <= needs[i] <= 10 // 1 <= special.length <= 100 // special[i].length == n + 1 // 0 <= special[i][j] <= 50
思路
其实本题思路很简单。就是暴力搜索。选或者不选大礼包,最终取一个最小值。
代码
jint res = 0;
public int shoppingOffers(List<Integer> price, List<List<Integer>> special, List<Integer> needs) {
res = noSpecial(price, needs);
backTrace(price, special, needs, 0, 0);
return res;
}
private int noSpecial(List<Integer> price, List<Integer> needs) {
int sum = 0;
for (int i = 0; i < needs.size(); i++) {
sum += price.get(i) * needs.get(i);
}
return sum;
}
private void backTrace(List<Integer> price, List<List<Integer>> special, List<Integer> needs, int index,
int cost) {
if (res < cost) {
return;
}
if (index == special.size()) {
cost += noSpecial(price, needs);
res = Math.min(cost, res);
return;
}
List<Integer> tmpSpecial = special.get(index);
if (checkValidate(tmpSpecial, needs)) {
List<Integer> newNeed = new ArrayList<>();
for (int j = 0; j < needs.size(); j++) {
newNeed.add(needs.get(j) - tmpSpecial.get(j));
}
backTrace(price, special, newNeed, index, cost + tmpSpecial.get(tmpSpecial.size() - 1));
}
backTrace(price, special, needs, index + 1, cost);
}
private boolean checkValidate(List<Integer> tmpSpecial, List<Integer> needs) {
for (int j = 0; j < needs.size(); j++) {
if (tmpSpecial.get(j) > needs.get(j)) {
return false;
}
}
return true;
}
代码2:回溯。这种方法与常规的套路一样。很容易想出来。
public int shoppingOffers(List<Integer> price, List<List<Integer>> special, List<Integer> needs) {
return backTrace(price, special, needs);
}
private int noSpecial(List<Integer> price, List<Integer> needs) {
int sum = 0;
for (int i = 0; i < needs.size(); i++) {
sum += price.get(i) * needs.get(i);
}
return sum;
}
private int backTrace(List<Integer> price, List<List<Integer>> special, List<Integer> needs) {
int noSpecial = noSpecial(price, needs);
int n = special.size();
for (int i = 0; i < n; i++) {
List<Integer> tmpSpecial = special.get(i);
if (checkValidate(tmpSpecial, needs)) {
for (int j = 0; j < needs.size(); j++) {
needs.set(j, needs.get(j) - tmpSpecial.get(j));
}
noSpecial = Math.min(backTrace(price, special, needs) + tmpSpecial.get(tmpSpecial.size() - 1), noSpecial);
for (int j = 0; j <needs.size(); j++) {
needs.set(j, needs.get(j) + tmpSpecial.get(j));
}
}
}
return noSpecial;
}
private boolean checkValidate(List<Integer> tmpSpecial, List<Integer> needs) {
for (int j = 0; j < needs.size(); j++) {
if (tmpSpecial.get(j) > needs.get(j)) {
return false;
}
}
return true;
}
}
总结
从这个题可以看出递归与回溯的区别
递归: 一条路走到底 回溯: 一条路走到底,不对时可以重新选择。因此多了一个撤销的过程。
backTrace(){
backTrace()
}
}
递归其实只是回溯其中的一步。
其实本题本质就是回溯。上面虽然看着是dfs,其实最后一步就类似于一个for循环,不过只有2个元素。
if (checkValidate(tmpSpecial, needs))
backTrace(price, special, needs, index + 1, cost);
|