題意:在一個國家裏,有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;
}