珍惜現在,感恩生活(九度教程第 103 題)
1.題目描述:
爲了挽救災區同胞的生命,心繫災區同胞的你準備自己採購一些糧食支援災
區,現在假設你一共有資金 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
2.基本思路
我們可以將該問題轉化爲0-1揹包問題,其時間複雜度爲.當然該辦法已經可以解決問題,但但對於k的規模較大的情況下可以採用更好的發來來進行優化。這個就得聯繫到之前涉及到的二進制拆解。舉個例子,對於k=7,其實我們沒有必要把這個對象存儲7次,反之只需要存儲1,2,4這三種組合便可以組合出1~7之間的任意情況了。因此對於更一般的情況k,可以將其拆解爲,其中,通過此番拆解我們可以將算法的時間複雜度降到.
3.代碼實現
#include <iostream>
#include <math.h>
#define M 2001
#define N 101
using namespace std;
struct rice{
int price;
int weight;
};
int IntPow(int x,int y){
int ans=1;
for(int i=1;i<=y;i++){
ans*=x;
}
return ans;
}
int max(int a,int b){return a>b?a:b;}
int dp[N];
rice list[M];
int main()
{
int C;
scanf("%d",&C);
int n,m;
int cnt=1;
for(int Case=1;Case<=C;Case++){
cnt=1;
scanf("%d%d",&n,&m);
int p,h,c;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&p,&h,&c);
int bound=int(log(c)/log(2));
for(int j=0;j<bound;j++){
list[cnt].price=IntPow(2,j)*p;
list[cnt].weight=IntPow(2,j)*h;
cnt++;
}
if(c-IntPow(2,bound)+1!=0){
list[cnt].price=(c-IntPow(2,bound)+1)*p;
list[cnt].weight=(c-IntPow(2,bound)+1)*h;
cnt++;
}
}
// for(int i=1;i<cnt;i++){
// printf("price=%d,weight=%d\n",list[i].price,list[i].weight);
// }
for(int i=0;i<cnt;i++)dp[i]=0;//初始化
for(int i=1;i<cnt;i++){
for(int j=n;j>=list[i].price;j--){
dp[j]=max(dp[j],dp[j-list[i].price]+list[i].weight);
}
}
printf("%d\n",dp[n]);
}
return 0;
}
/*
1
8 2
2 100 4
4 100 2
*/