大礼包
LeetCode638题,购买指定数量的商品,可以单独购买,也可以购买套餐,问最优惠的购买方式需要花费多少钱。
在LeetCode商店中, 有许多在售的物品。
然而,也有一些大礼包,每个大礼包以优惠的价格捆绑销售一组物品。
现给定每个物品的价格,每个大礼包包含物品的清单,以及待购物品清单。请输出确切完成待购清单的最低花费。
每个大礼包的由一个数组中的一组数据描述,最后一个数字代表大礼包的价格,其他数字分别表示内含的其他种类物品的数量。
任意大礼包可无限次购买。
示例 1:
输入: [2,5], [[3,0,5],[1,2,10]], [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:
输入: [2,3,4], [[1,1,0,4],[2,2,1,9]], [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更加便宜。
说明:
最多6种物品, 100种大礼包。
每种物品,你最多只需要购买6个。
你不可以购买超出待购清单的物品,即使更便宜。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shopping-offers
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路分析:
1.从现有礼包中选择符合条件的礼包(不能出现某商品数量大于需求商品的数量)
2.同款礼包可以多次选择,并且同款礼包的选择次数不同,对最终计算的价格也不同,原因在于可以选择不同的礼包(迭代)
3.最终礼包的数量需要单独付款时,需要单独计算
private static int res = 0;
private static int sum = 0;
private static List<Integer> global_needs;
private static List<Integer> global_price;
private static List<List<Integer>> global_special;
public static int shoppingOffers(List<Integer> price, List<List<Integer>> special, List<Integer> needs) {
for (int i = 0; i < needs.size(); i++) {
res += price.get(i) * needs.get(i);
}
global_needs = new ArrayList<>(needs);
global_price = new ArrayList<>(price);
global_special = new ArrayList<>(special);
find(0);
return res;
}
public static void find(int index) {
for (int i = index; i < global_special.size(); i++) {
List<Integer> item = global_special.get(i);
//计算当前下标的礼包可以购买的最大数量
int buyMaxSum = cal_special_num(item);
if (buyMaxSum != 0) {
//至少可以购买一次,需要循环同一个礼包购买的次数,会产生其余礼包的不同购买
for (int j = 1; j <= buyMaxSum; j++) {
//记录目前的金额和所需的商品的剩余数量
int tempSum = sum;
List<Integer> tempList = new ArrayList<>(global_needs);
//当前金额加当前礼包的价格乘数量
sum += item.get(item.size() - 1) * j;
for (int k = 0; k < global_price.size(); k++) {
//所需商品的剩余数量减去对应礼包中的商品数量乘礼包数
global_needs.set(k, global_needs.get(k) - item.get(k) * j);
}
//迭代寻找下一个套餐
find(i + 1);
//最终返回的global_needs肯定是所有套餐都不满足的
for (int k = 0; k < global_needs.size(); k++) {
//计算单个购买的价格
sum += global_needs.get(k) * global_price.get(k);
}
//比较目前最小的价格和当前价格的大小
res = Math.min(res, sum);
//恢复开始的金额和剩余数量,j++则多购买一个当前数量的礼包
sum = tempSum;
global_needs = tempList;
}
}
}
}
private static int cal_special_num(List<Integer> item) {
int max = Integer.MAX_VALUE;
for (int i = 0; i < global_needs.size(); ++i) {
if (global_needs.get(i) < item.get(i))
return 0;
if (item.get(i) != 0)
max = Math.min(global_needs.get(i) / item.get(i), max);
}
return max;
}
正方形数组的数目
一个数组允许调换元素的顺序,使得相邻元素相加得到的数是一个完全平方根,问不同的排列有几种?
思路分析:该题的难点在于如何保证,这次满足正方形数组的顺序,不和前面的情况一致,关于寻找正方形数组的问题,只需要通过迭代即可。我们可以把数组排序,这样相同的数就聚在一起了,通过判断来达到去重的目的。
private static int res;
private static boolean visited[];
public static void main(String[] args) {
System.out.println(numSquarefulPerms(new int[]{5,11,5,4,5}));
}
public static int numSquarefulPerms(int[] A) {
//排序后将相同元素放在一起
Arrays.sort(A);
visited = new boolean[A.length];
dfs(A, -1, 0);
return res;
}
private static void dfs(int[] arr, int pre, int count) {
if (count >= arr.length) {
res++;
}
for (int i = 0; i < arr.length; i++) {
// 减枝+去重
if (visited[i] || (pre != -1 && !isSquare(pre + arr[i])) || (i != 0 && arr[i] == arr[i - 1] && !visited[i - 1])) {
continue;
}
visited[i] = true;
dfs(arr, arr[i], count + 1);
visited[i] = false;
}
}
// 用来判断是否是完全平方数
private static boolean isSquare(int num) {
double sqrt = Math.sqrt(num);
return sqrt - (int) sqrt == 0;
}