638.大禮包

在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)


這題咋說呢,雖然寫了一個小時左右,但還真是中等裏面獨立思考和完成的一道題。
趕緊寫一下此題的思路形成:
1.這個題要遞歸。遞歸要怎麼解決呢?
2.奧,需要遞歸列出所有的可能解,找出最小的答案就可以了。
3.遞歸的幾個要素要怎麼寫呢?

  • 結束條件:不能多買,要正好纔行。後來又發展到,沒有剩餘即可(要買的都買完了)。
    中間有個過程,是思考了一下這個遞歸要解決的問題把東西買完了就行。想到這裏恍然大悟,所以結束條件必然是東西買完了就可以了。開始想的是遞歸的結束條件,而沒有想到用這個遞歸來解決的實際問題。脫離了實際。下次不妨想想,這個事情我做到啥程度纔不做了
  • 主體:主題必然是買東西了
    • 首先先買大禮包,之後再買單品。
    • 買大禮包要看看能不能買。能買,遞歸;不能買,看下一個大禮包。
    • 最後查漏補缺,把沒買的買單品。
    • 寫完猶豫了一下,應該是大禮包和單品的優先級 (當時理解成代碼的執行順序 )是一樣的,應該放在一起。後來又一想,大禮包肯定優惠啊。(2020.5.5現在想想,這一條就是錯的。不存在優不優惠。)

4 . 最後發現答案不對,想了想原來是中間一個變量錯了( ans-=special[i][j]; 沒有這一行)。改了,成功。
最後關於那個優先級問題,加上 ans-=special[i][j],後來一想,這樣優先級就一樣了。現在的理解是,就是都有機會買到而已。
或者改爲dfs(price,special,needs,ans+special[i][j])即可。

int eans=0x3f3f3f3f;
    int shoppingOffers(vector<int>& price, vector<vector<int>>& special, vector<int>& needs) {
      
        dfs(price,special,needs,0);
        return eans;
    }
    void dfs(vector<int>& price, vector<vector<int>>& special, vector<int>& needs,int ans){
        int flag=0;
        for(auto a:needs){
            flag+=a;
        }
        if(flag==0){
            eans=min(ans,eans);
            return;
        } 
        //需要的  都  買完了。所以結束遞歸,返回價錢。

        //開始買大禮包
        for(int i=0;i<special.size();i++){
            //看看大禮包能不能買,不能買的的話,看下一個大禮包
            int flag1=true;
            int j;
            for(j=0;j<needs.size();j++){
                if(special[i][j]>needs[j]) {
                    flag1=false;
                    break;
                }
            }

            //要是可以買大禮包,則遞歸
            if(flag1){
                ans+=special[i][j];//把大禮包的價錢加上
                for(j=0;j<special[i].size()-1;j++) needs[j]-=special[i][j];//把需求減去
                dfs(price,special,needs,ans);//繼續看下一個禮包,
                //遞歸之後,需要需求加回來
                for(j=0;j<special[i].size()-1;j++) needs[j]+=special[i][j];
                ans-=special[i][j];//把大禮包的價錢減去
            }
        }
        //大禮包買完了,開始買單價商品
        vector<int>tep=needs;
        int tepp=0;
        for(int i=0;i<needs.size();i++){
            if(needs[i]!=0){
                tepp+=needs[i]*price[i];
                needs[i]=0;
            }
        }
        ans+=tepp;
        //cout<<ans<<endl;
        dfs(price,special,needs,ans);
        needs=tep;
        ans-=tepp;
    }

一個精簡的代碼

class Solution {
public:
    int shoppingOffers(vector<int>& price, vector<vector<int>>& special, vector<int>& needs) {
         return shopping(price,special,needs);
    }
    int shopping(vector<int>& price, vector<vector<int>>& special, vector<int>& needs){
        int obo=getSum(price,needs);//單價買
        for(vector<int> giftPack:special){//循環查看每個大禮包
            vector<int> cloneNeeds(needs);
            int i;
            for( i=0;i<needs.size();i++){
                int remain=cloneNeeds[i]-giftPack[i];
                if(remain<0){//此禮包不可買
                    break;
                }
                cloneNeeds[i]=remain;
            }
            if(i==needs.size()) //此禮包可以買
            obo=min(obo,shopping(price,special,cloneNeeds)+giftPack[i]);
        }
        return obo;
    }
    int getSum(vector<int>& price,vector<int>& needs){
        int sum=0;
        for(int i=0;i<price.size();i++){
            sum+=price[i]*needs[i];
        }
        return sum;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章