珍惜現在,感恩生活 - 九度教程第 103 題
題目
時間限制:1 秒 內存限制:32 兆 特殊判題:否
題目描述:
爲了挽救災區同胞的生命,心繫災區同胞的你準備自己採購一些糧食支援災區,現在假設你一共有資金 n 元,而市場有 m 種大米,每種大米都是袋裝產品,其價格不等,並且只能整袋購買。請問:你用有限的資金最多能採購多少公斤糧食呢?
輸入:
輸入數據首先包含一個正整數 C,表示有 C 組測試用例,每組測試用例的第一行是兩個整數 n 和 m(1<=n<=100, 1<=m<=100),分別表示經費的金額和大米的種類,然後是 m 行數據,每行包含 3 個 數 p , h 和c(1<=p<=20,1<=h<=200,1<=c<=20),分別表示每袋的價格、每袋的重量以及對應種類大米的袋數。
輸出:
對於每組測試數據,請輸出能夠購買大米的最多重量,你可以假設經費買不光所有的大米,並且經費你可以不用完。每個實例的輸出佔一行。
樣例輸入:
1
8 2
2 100 4
4 100 2
樣例輸出:
400
在該題中對每個物品的總數量進行了限制,即多重揹包問題。對每種物品進行拆分,使物品數量大大減少,同時通過拆分後的物品間的組合又可以組合出所有物品數量的情況。
#include <stdio.h>
struct E{//大米
int w;//價格
int v;//重量
}list[2001];
int dp[101];
int max(int a,int b){
return a>b ? a : b;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
int s,n;//經費的金額和大米的種類
scanf("%d%d",&s,&n);
int cnt=0;//拆分後物品總數
for(int i=1;i<=n;i++){
int w,v,k;
//表示每袋的價格、每袋的重量
//以及對應種類大米的袋數
scanf("%d%d%d",&w,&v,&k);
int c=1;
while(k-c>0){
//對輸入的數字k,拆分成1,2,4...k-2^k+1,
//其中c爲使最後一項大於0的最大整數
k-=c;
list[++cnt].w=c*w;
list[cnt].v=c*v;
//拆分後的大米重量和價格均爲組成該物品
//的大米重量價格和
c*=2;
}
list[++cnt].w=w*k;
list[cnt].v=v*k;
}
for(int i=1;i<=s;i++){
dp[i]=0;//初始值
}
for(int i=1;i<=cnt;i++){
//對拆分後的所有物品進行0-1揹包
for(int j=s;j>=list[i].w;j--){
dp[j]=max(dp[j],dp[j-list[i].w]+list[i].v);
}
}
printf("%d\n",dp[s]);
}
return 0;
}
總結多重揹包問題:多重揹包的特徵是每個物品可取的數量爲一個確定的整數,通過對這個整數進行拆分,使若干個物品組合成一個價值和體積均爲這幾個物品的和的大物品,同時通過這些大物品的間的組合又可以組合出選擇任意件物品所包含的體積和重量情況,通過這種拆分使最後進行 0-1 揹包的物品數量大大減少,從而降低複雜度,其時間複雜度爲:
空間複雜度與 0-1 揹包保持一致。
本節主要討論了揹包問題,重點指出了三個類型的揹包問題,0-1 揹包、完全揹包、多重揹包,其中 0-1 揹包是揹包問題的基礎,很多揹包問題都可以轉化到 0-1 揹包上來。