题目链接
思路一:dfs+剪枝
剪枝的思路就是:1.购买整个礼包花的钱比单买便宜 2.购买整个礼包不会使得商品数大于对应的所需商品数
class Solution {
private int min = Integer.MAX_VALUE;
private List<Integer> price;
private List<List<Integer>> special;
//记录某个礼包是否有优惠
boolean[] discount;
public int shoppingOffers(List<Integer> _price, List<List<Integer>> _special, List<Integer> needs) {
this.price = _price;
this.special = _special;
discount = new boolean[_special.size()];
//剪枝的准备工作
for(int i = 0; i<_special.size();i++){
List<Integer> singleSpecial = _special.get(i);
int spend = 0;
for(int j = 0; j<singleSpecial.size();j++){
if(j == singleSpecial.size()-1 ){
if(spend > singleSpecial.get(j)){
//有优惠
discount[i] = true;
}
}else{
//计算单个买要花的钱
spend += (singleSpecial.get(j) * _price.get(j));
}
}
}
dfs(needs,0);
return min;
}
public void dfs(List<Integer> needs,int money){
if(money>min){
return;
}
//尝试每一种大礼包,看看哪一种符合就用哪一种
for(int i = 0; i < special.size(); ++i){
if(!discount[i]){
continue;
}
//取出第i个礼包
List<Integer> list = special.get(i);
//用来记录下一层需要的商品个数
List<Integer> cur = new ArrayList<>();
//循环礼包中的每个商品的数量
for(int j = 0; j < list.size(); ++j){
if(j==list.size()-1){
//购买了第i个礼包,进入下一层
dfs(cur,money+list.get(j));
} else {
//num记录第j个礼包所需要的数量
int num = needs.get(j);
//当所需商品的数量小于了这个礼包中这个商品的数量直接不能买这个礼包
if(num<list.get(j)) break;
//否则,所需的减少数量
cur.add(num-list.get(j));
}
}
}
//走到这里说明没有符合的大礼包了,直接减去每个的单价就行
int m = money;
for(int i = 0; i < needs.size(); ++i){
m += needs.get(i) * price.get(i);
}
min = Math.min(min,m);
}
}
思路二:通过数组表示某个礼包买多少个,然后枚举每个礼包从0–10个,然后剪枝,也是dfs+剪枝,不过角度不一样。
class Solution {
private int min = Integer.MAX_VALUE;
private List<Integer> price;
private List<List<Integer>> special;
boolean[] discount;
public int shoppingOffers(List<Integer> _price, List<List<Integer>> _special, List<Integer> needs) {
this.price = _price;
this.special = _special;
discount = new boolean[_special.size()];
//剪枝的准备工作
for(int i = 0; i<_special.size();i++){
List<Integer> singleSpecial = _special.get(i);
int spend = 0;
for(int j = 0; j<singleSpecial.size();j++){
if(j == singleSpecial.size()-1 ){
if(spend > singleSpecial.get(j)){
//有优惠
discount[i] = true;
}
}else{
//计算单个买要花的钱
spend += (singleSpecial.get(j) * _price.get(j));
}
}
}
dfsII(needs,0,0);
return min;
}
/**
*
* @param needs
* @param money
* @param curIndex 当前礼包的下标
*/
public void dfsII(List<Integer> needs,int money,int curIndex){
if(money > min){
return;
}
//没有越界
if(curIndex<special.size()){
boolean overflow = false;
//购买第curIndex礼包count个
for(int count = 0; count < 10;count++){
if(!discount[curIndex]){
dfsII(needs,money,curIndex+1);
break;
}
//取出第curIndex个礼包
List<Integer> list = special.get(curIndex);
//用来记录下一层需要的商品个数
List<Integer> cur = new ArrayList<>();
//循环礼包中的每个商品的数量
for(int j = 0; j < list.size(); ++j){
if(j==list.size()-1){
//购买了第curIndex个i个礼包,进入下一层
dfsII(cur,money+list.get(j)*count,curIndex+1);
} else {
//num记录第j个商品所需要的数量
int num = needs.get(j);
//当所需商品的数量小于了这个礼包中这个商品的数量直接不能买这个礼包
if(num<list.get(j)*count) {
overflow = true;
break;
}
//否则,所需的减少数量
cur.add(num-list.get(j)*count);
}
}
if(overflow){
//表示这个礼包不能买count个了,只能小于count个
break;
}
}
}
if(min<=money){
return;
}
for(int i = 0; i < needs.size();i++){
money += (needs.get(i) * price.get(i));
}
min = Math.min(min,money);
}
}