1. 程式人生 > >LintCode 800: Backpack IX (經典01揹包問題,DP)

LintCode 800: Backpack IX (經典01揹包問題,DP)

注意,該題是01揹包問題的變種,不是完全揹包問題!因為每個學校只能申請一次!

class Solution {
public:
    /**
     * @param n: Your money
     * @param prices: Cost of each university application
     * @param probability: Probability of getting the University's offer
     * @return: the  highest probability
     */
    double backpackIX(int n, vector<int> &prices, vector<double> &probability) {
        vector<double> dp(n + 1, 1.0);
        //dp[x] is the min probability that no one offer is received when money x are used.
        int count = prices.size();
        
        for (int i = 0; i < count; ++i) {
            //for (int j = prices[i]; j <= n; ++j) {    //錯誤!
            for (int j = n; j >= prices[i]; --j) {
                dp[j] = min(dp[j], dp[j - prices[i]] * (1.0 - probability[i]));
                //cout<<"i = "<<i<<" j="<<j<<" "<<dp[j]<<endl;
            }
        }        
        return 1 - dp[n];
    }
};

以 Input 10 [4,4,5] [0.1,0.2,0.3] 為例,則輸出為: i = 0 j=10 0.9 i = 0 j=9 0.9 i = 0 j=8 0.9 i = 0 j=7 0.9 i = 0 j=6 0.9 i = 0 j=5 0.9 i = 0 j=4 0.9 i = 1 j=10 0.72 i = 1 j=9 0.72 i = 1 j=8 0.72 i = 1 j=7 0.8 i = 1 j=6 0.8 i = 1 j=5 0.8 i = 1 j=4 0.8 i = 2 j=10 0.56 //min(dp[10]=0.72, dp[5](1-price[2])=0.80.7) i = 2 j=9 0.56 i = 2 j=8 0.7 i = 2 j=7 0.7 i = 2 j=6 0.7 i = 2 j=5 0.7 Output 0.44

這個0.56是怎麼來的呢?我們要讓最終答案(0.44)儘量大,那麼dp[10]=0.56就要儘量小。

注意,這裡的j迴圈一定要是從大到小。為什麼呢? 我們將j迴圈換成下面的程式碼: for (int j = prices[i]; j <= n; ++j) { //注意,這是錯的! 以 上面的Input為例,則輸出為:

i = 0 j=4 0.9 i = 0 j=5 0.9 i = 0 j=6 0.9 i = 0 j=7 0.9 i = 0 j=8 0.81 i = 0 j=9 0.81 i = 0 j=10 0.81 i = 1 j=4 0.8 i = 1 j=5 0.8 i = 1 j=6 0.8 i = 1 j=7 0.8 i = 1 j=8 0.64 i = 1 j=9 0.64 i = 1 j=10 0.64 i = 2 j=5 0.7 i = 2 j=6 0.7 i = 2 j=7 0.7 i = 2 j=8 0.64 i = 2 j=9 0.56 i = 2 j=10 0.49

Output 0.51 Expected 0.44

注意01揹包和多重揹包的j迴圈是從大到小,完全揹包的j迴圈是從小到大!。