HDU 3466 Proud Merchants 01揹包 單機調度問題

題意:在一個國家裏,有N件商品。每件商品有價格Pi,價值Vi,和一個神奇的屬性Qi。Qi表示,當你的錢大於等於Qi的時候,才能買商品i。現在你有M元錢,希望你最大化買到的東西的價值和。

思路:當沒有屬性Qi的時候,就是個標準的01揹包。

           當Qi被引入的時候,會發生什麼變化呢?對於狀態轉移方程, dp[j] = max(dp[j],dp[j-p[i]]+v[i]).當沒有Qi的時候,j的循環範圍是M>=j>=p[i]。當有Qi的時候,j的循環範圍就是M>=j>=q[i]。即縮小了對狀態更新的範圍。

           從這個角度來是,我們爲了得到最優的解,就要在讓更新的範圍從大到小,而這就需要對Qi - Pi從小到大進行排序,然後用01揹包求解就行了。

           但是上面的解釋還是有點牽強的,最好的解釋該從單機調度問題的角度來證明需要對Qi - Pi從小到大進行排序。

           具體解釋過程見:

           http://blog.csdn.net/oceanlight/article/details/7866759

           在上面博客中的論述中的兩部分,對M的排序是是相反的。出現這樣的情況的原因,是在01揹包的狀態轉移中,dp[j]要利用dp[j-p[i]],所以dp[j-p[i]]是dp[j]子問題,這樣從最原始的遞歸求解的過程中,反倒是最後一個商品是最先加入的。這點要非常注意。

代碼如下:

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int MAX = 550;

struct node{
    int p,q,v;
    bool operator < (const node & rhs) const{
        return q - p < rhs.q - rhs.p;
    }
} a[MAX];

int dp[5500];

int main(void)
{
    //freopen("input.txt","r",stdin);
    int N,M;
    while(scanf("%d%d",&N,&M) != EOF){
        for(int i = 0; i < N; ++i)
            scanf("%d%d%d",&a[i].p,&a[i].q,&a[i].v);
        sort(a,a+N);
        memset(dp,0,sizeof(dp));
        for(int i = 0; i < N; ++i)
            for(int j = M; j >= a[i].q; --j)
                dp[j] = max(dp[j],dp[j-a[i].p] + a[i].v);
        printf("%d\n",dp[M]);
    }
    return 0;
}


發佈了283 篇原創文章 · 獲贊 4 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章