题目 638. 大礼包
解题思路
看题第一眼,想到了贪心,细品,想到了动态规划,再细品,还是回溯吧。。。菜鸡的自我救赎,能暴力就暴力hhhhh。
1. 难点在于每个礼包可以选无数次,用动态规划不太好做
2.对于这种可以选多次的最优化问题,回溯最好想,而且数据量也不大,不会超时
回溯优化:剪枝。ans记录当前的最小花费,一旦某条支路上的money超过了ans,这条支路可以毫不犹豫的舍弃。c++代码如下:
class Solution {
public:
int ans=0;//记录用的钱
int shoppingOffers(vector<int>& price, vector<vector<int>>& special, vector<int>& needs) {
int n=price.size();
int gift_num=special.size();
for(int i=0;i<n;i++){//不买任何礼包时所花的钱,然后再用这个去和使用大礼包时比较,选出价格最低的
ans+=price[i]*needs[i];
}
int money=0;
Try(0,price, special, needs, money);
return ans;
}
bool Safe(vector<int> gift,vector<int> needs){
int n=needs.size();
for(int i=0;i<n;i++){
if(gift[i]>needs[i]) return false;
}
return true;
}
void Record(vector<int> gift,vector<int>& needs){
int n=needs.size();
for(int i=0;i<n;i++){
needs[i]-=gift[i];
}
return ;
}
void Delete(vector<int> gift,vector<int>& needs){
int n=needs.size();
for(int i=0;i<n;i++){
needs[i]+=gift[i];
}
return ;
}
bool isEnd(vector<int> needs){
for(int i=0;i<needs.size();i++){
if(needs[i]>0) return false;
}
return true;
}
void Try(int k, vector<int>& price,vector<vector<int>>& special, vector<int>& needs,int money)
{
int n=price.size();
int gift_num=special.size();
if(isEnd(needs)){//已经买完了
ans=min(ans,money);
return ;
}
if(money>ans)//剪枝
{
return ;
}
int flag=0;
for(int i=0;i<gift_num+1;i++)
{
if(i<gift_num&&Safe(special[i],needs)){
flag=1;
Record(special[i],needs);
money+=special[i][n];
Try(k+1,price, special, needs, money);
//回溯
Delete(special[i],needs);
money-=special[i][n];
}
if(i==gift_num){
int m=0;
for(int i=0;i<n;i++){
m+=price[i]*needs[i];
}
ans=min(ans,money+m);
}
}
if(flag==0)//已经不能买任何一个大礼包,但是此时还有东西没买完
{
for(int i=0;i<n;i++){
money+=price[i]*needs[i];
}
ans=min(ans,money);
return ;
}
}
};