珍惜现在,感恩生活(九度教程第 103 题)

珍惜现在,感恩生活(九度教程第 103 题)

时间限制:1 秒 内存限制:32 兆 特殊判题:否

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背包问题,其时间复杂度为 O ( c ∗ ∑ i = 1 n k i ) O(c*\sum\limits_{i=1}^nk_i) O(ci=1nki).当然该办法已经可以解决问题,但但对于k的规模较大的情况下可以采用更好的发来来进行优化。这个就得联系到之前涉及到的二进制拆解。举个例子,对于k=7,其实我们没有必要把这个对象存储7次,反之只需要存储1,2,4这三种组合便可以组合出1~7之间的任意情况了。因此对于更一般的情况k,可以将其拆解为 1 , 2 , 4 , . . . , k − 2 c + 1 1,2,4,...,k-2^c+1 1,2,4,...,k2c+1,其中 c = ⌊ log ⁡ 2 k ⌋ c=\lfloor \log_2^k\rfloor c=log2k,通过此番拆解我们可以将算法的时间复杂度降到 O ( c ∗ ∑ i = 1 n l o g 2 ( k i ) ) O(c*\sum\limits_{i=1}^nlog_2(k_i)) O(ci=1nlog2(ki)).

3.代码实现

#include 
#include 
#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
//            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
*/

下面这个版本的完全背包代码更加的简洁

#include 
#define N 100+5
using namespace std;

int dp[N];
int main()
{
    int C,n,m;
    cin>>C;
    while(C--){

        cin>>n>>m;
        int p,w,num;

        for(int i=0;i<=n;i++)dp[i]=0;
        
        for(int i=1;i<=m;i++){
            cin>>p>>w>>num;
            if(p*num>=n){//完全背包
                for(int j=p;j<=n;j++){
                    dp[j]=max(dp[j],dp[j-p]+w);
                }
            }
            else{//多重背包
                for(int k=1;num-k>=0;k*=2){
                    for(int j=n;j>=p*k;j--){
                        dp[j]=max(dp[j],dp[j-p*k]+w*k);
                    }
                    num-=k;
                }
                if(num>0){
                    for(int j=n;j>=p*num;j--){
                        dp[j]=max(dp[j],dp[j-p*num]+w*num);
                    }
                }
            }
        }
        cout<<dp[n]<<endl;
    }
    
    return 0;
}

/*
1
8 2
2 100 4
4 100 2
*/

你可能感兴趣的:(算法刷题集)