HDU #2191 买米问题 多重背包及其优化

Description



  问题描述以及测试样例在这:HDU#2191

思路



  这道题其实就是多重背包问题,即有 N 种物品和一个容量为 V 的背包,第 i 种物品最多有 n[i] 件可用,每件费用是 c[i] ,价值是 w[i] ,求哪些物品装入背包可以使得这些物品的费用总和不超过背包容量,且价值总和最大。在这道题中,背包容量是经费,费用是每种米的价格,价值是每种米的重量,求用给定的经费买米,使得买到米的总重量最大,注意经费是可以剩余的。

  其实多重背包问题和完全背包问题非常相似,只不过前者的物品是有限件而后者物品是无限件。我们可以把完全背包的状态转移方程改造一下而得到多重背包的状态转移方程,如下:

dp[i][j] = max { dp[i-1][v-k*c[i]] + k*w[i] | 0 <= k <= n[i] }

  算法通过穷举 k 而得到所有的解,然后在从其中调出最大的。我们根据它很容易就可以写出算法,时间复杂度为 O( V·∑n[i] ) :

#include<iostream>
#include<algorithm>
using namespace std;
const int MAX_N = 100; //经费价格上限
const int MAX_M = 100; //大米种类上限
int price[MAX_M+1]; //价格
int weight[MAX_M+1]; //重量
int num[MAX_M+1]; //袋数
int dp[MAX_M+1][MAX_N+1] = {0}; //最大重量

int main(void) {
    int test_num;
    cin >> test_num;
    while (test_num--) {
        int n, m; //经费金额, 大米种类
        cin >> n >> m;
        for (int i = 1; i <= m; ++i) {
            cin >> price[i] >> weight[i] >> num[i];
        }
        //用j经费买前i种米,能买到的最大总重量
        for (int i = 1; i <= m; i++){
            for (int j = 1; j <= n; j++) {
                int max_weight = 0;
                for (int k = 0; k <= num[i]; k++) {
                    if (j-k*price[i] >= 0) {
                        if (max_weight < dp[i-1][j-k*price[i]] + k*weight[i] ) {
                            max_weight = dp[i-1][j-k*price[i]] + k*weight[i];
                        }
                    }
                }
                dp[i][j] = max_weight;
            }
        }
        cout << dp[m][n] << endl;
    }
    return 0;
}

  然而,它和自顶向下的递归算法一比,时间和空间并没有得到优化,所以我先从空间上去优化这个算法。

  由于01背包问题和完全背包问题都有一维数组实现的算法,那么我们可以试着把多重背包问题转化成其中一个问题再进行求解。我先把它转化成好写且好理解的01背包问题。在01背包问题中需要我们以“件”为单位而不是以“种”为单位去看待物品,所以需要把一种物品拆分成多件物品。

  如何拆分呢?

  在多重背包里,直接把第 i 种物品拆分成 n[i] 件就好啦。

  额外再说个完全背包问题中拆分物品的方法。如果物品的个数是 N ,背包的容量是 V ,第 i 种物品的费用是 cost[i] ,那么应该把第 i 种物品拆分成 V/cost[i] 件。举个例子,如果物品个数是 3 ,背包的容量是 5 ,那么拆分前的物品序列是下面图左,拆分后的物品序列是下面的图右:

          

  

  拆分好之后,就直接套用一维滚动数组解决01背包问题的算法即可,空间复杂度优化到为 O(V) :

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAX_N = 100; //经费价格上限
const int MAX_M = 100; //大米种类上限
int price[MAX_M+1]; //价格
int weight[MAX_M+1]; //重量
int num[MAX_M+1]; //袋数
int dp[MAX_N+1]; //最大重量

int main(void) {
    int test_num;
    cin >> test_num;
    while (test_num--) {
        int n, m; //经费金额, 大米种类
        cin >> n >> m;
        for (int i = 1; i <= m; ++i) {
            cin >> price[i] >> weight[i] >> num[i];
        }
        //初始化dp
        memset(dp, 0, sizeof(dp));
        //用j经费买前i种米,能买到的最大总重量
        for (int i = 1; i <= m; i++){
            //对第i种米进行num[i]次01选择
            for (int k = 1; k <= num[i]; k++) {
                for (int j = n; j >= price[i]; j--) {
                    //要么选,要么不选
                    dp[j] = std::max(dp[j], dp[j-price[i]] + weight[i] );
                }
            }
        }
        /*
        cout << "检查中" << endl;
        for (int i = 0; i <= m; i++) {
            for (int j = 0; j <= n; j++){
                cout << dp[i][j]  << "   ";
            }
            cout << endl;
        }
        */
        cout << dp[n] << endl;
    }
    return 0;
}

  如何优化时间复杂度呢?这个我暂时没有思路,之后若有了话再继续写博。

 

原文地址:https://www.cnblogs.com/Bw98blogs/p/8407434.html

时间: 2024-11-14 14:12:55

HDU #2191 买米问题 多重背包及其优化的相关文章

HDU 2191 悼念512【多重背包+二进制优化】

大意分析: 多重背包,转化为01背包即可 可以用二进制进行优化 代码:(代码没有优化,下题是优化才可过的) 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 const int maxn = 105; 7 8 int n, m, tot; 9 int p[2005], h[2005]; 10 int dp[maxn]; 11 int solv

HDU 1059 多重背包+二进制优化

Dividing Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 16909    Accepted Submission(s): 4729 Problem Description Marsha and Bill own a collection of marbles. They want to split the collection

台州 OJ 2537 Charlie&#39;s Change 多重背包 二进制优化 路径记录

描述 Charlie is a driver of Advanced Cargo Movement, Ltd. Charlie drives a lot and so he often buys coffee at coffee vending machines at motorests. Charlie hates change. That is basically the setup of your next task. Your program will be given numbers

[多重背包+二进制优化]HDU1059 Dividing

题目链接 题目大意: 两个人要把一堆宝珠,在不能切割的情况下按照价值平分,他们把宝珠分成6种价值,每种价值的宝珠n个. n<=200000 思考: 首先如果加和下来的价值是一个偶数 那么还分毛啊,直接gg. 之后多重背包二进制优化 转换为 01背包. 我们可以把价值 同时当做宝珠的空间和价值. 那么我们现在要求的是 在 空间为一半的情况下,能否找到价值为 一半的情况. 1 #include <cstdio> 2 #include <algorithm> 3 #include

14年省赛---多重部分和问题(多重背包+二进制优化)

1210: F.多重部分和问题 时间限制: 1 Sec  内存限制: 64 MB提交: 18  解决: 14 题目描述 有n种不同大小的数字,每种各个.判断是否可以从这些数字之中选出若干使它们的和恰好为K. 输入 首先是一个正整数T(1<=T<=100)接下来是T组数据 每组数据第一行是一个正整数n(1<=n<=100),表示有n种不同大小的数字 第二行是n个不同大小的正整数ai(1<=ai<=100000)第三行是n个正整数mi(1<=mi<=100000

hdu1059 dp(多重背包二进制优化)

hdu1059 题意,现在有价值为1.2.3.4.5.6的石头若干块,块数已知,问能否将这些石头分成两堆,且两堆价值相等. 很显然,愚蠢的我一开始并想不到什么多重背包二进制优化```因为我连听都没有听过```不得不吐槽自己的知识面太窄```于是,我用了母函数写这题,母函数的做法并没有问题,但是由于这道题的数据很大,母函数轻轻松松就超时了,于是,我又很努力地在母函数循环的优化上面想出路,改改改,各种改之后依旧TLE,01背包的做法显然也是会超时的,DISCUSS里的母函数做法优化方式都是模上一个大

HDU 3732 Ahui Writes Word(多重背包)

http://acm.hdu.edu.cn/showproblem.php?pid=3732 题意: 初始有N个物品, 每个物品有cost[i]花费和val[i]价值, 你有m元钱, 现在问你最多能买多少总价值的物品? 其中N<=10W, m<=1W. 且cost[i]和val[i]都在[0,10]范围. 分析: 本题初看直接用01背包来做是直观的想法. 但是考虑到01背包的复杂度为O(N*m), 这么大的复杂度肯定不行. 然后我们发现其实每种物品只与它的cost[i]和val[i]有关, 如

HDU 2159 FATE (二维多重背包)

FATE Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 9352    Accepted Submission(s): 4413 Problem Description 最近xhd正在玩一款叫做FATE的游戏,为了得到极品装备,xhd在不停的杀怪做任务.久而久之xhd开始对杀怪产生的厌恶感,但又不得不通过杀怪来升完这最后一级.现在的问

HDU 2082 找单词 (多重背包)

题意:假设有x1个字母A, x2个字母B,..... x26个字母Z,同时假设字母A的价值为1,字母B的价值为2,..... 字母Z的价值为26.那么,对于给定的字母,可以找到多少价值<=50的单词呢?单词的价值就是组成一个单词的所有字母的价值之和,比如,单词ACM的价值是1+3+14=18,单词HDU的价值是8+4+21=33.(组成的单词与排列顺序无关,比如ACM与CMA认为是同一个单词). 题解:把26个字母看做26种背包,有个数有价值,求价值不超过50的所有可能个数,就是标准的多重背包.