[Sicily Coins] 动态规划 多重背包问题

1. 问题描述

Description

Ouyang has 6 kinds of coins.
The number of the i-th coin is A[i] (0<=i<6).
Their value and weight are as follewed:
0. $0.01, 3g
1. $0.05, 5g
2. $0.10, 2g
3. $0.25, 6g
4. $0.50, 11g
5. $1, 8g
Ouyang want to run away from home with his coins.
But he is so weak that he can only carry M gram of coins.
Given the number of each coin he has, what is the maximal value of coins he can take?

Input

There are multiple cases.
Each case has one line with 7 integers: M (1<=M<=10000), A[i], (0<=i<6, 0<=A[i]<=100000).

Output

The maximal value of coins he can take.

Sample Input

1 1 1 1 1 1 1
38 3 1 10 4 2 1
75 8 5 23 4 2 4

Sample Output

$0.00
$2.40
$6.10

2. 基本思路

回顾:0-1背包问题

  • 在0-1背包问题中,给定n个物品,第i个物品有重量w[i]和价值v[i],用容量为W的背包来装物品,总价值最大是多少?
  • OPT(i, w)表示:背包容量为w时,考虑给定第1, 2, …, i个物品,能取得的最大价值。装物品的时候有以下两种情况:第一种情况是不选择第i个物品,那么最大价值将会是:容量为w,考虑物品{1, 2, …, i-1}所取得的价值;第二种情况是选择第i个物品,那么最大价值将会是:容量为(w-w[i]),考虑物品{1, 2, …, i-1}所取得的价值加上当前第i个物品的价值v[i]。
  • 0-1背包问题的代码实现如下 :
for (int w = 0; w <= W; w++)
{
    M[0, w] = 0;
}
for (int i = 1; i <= n; i++)
{
    for (w = 1; w <= W; w++)
    {
        if (wt[i] > w)
        {
            M[i, w] = M[i - 1, w];
        }
        else
        {
            M[i, w] = max(M[i - 1][w], v[i] + M[i - 1, w - wt[i]]);
        }
    }
}

多重背包问题

  • 以上的Coins问题属于多重背包问题。多重背包问题与0-1背包问题的不同之处在于:多重背包问题每种类型的物品可以有多个。
  • 在0-1背包的基础上加入以下内容即可实现:
M[i][w] = 0;
int nCount = min(A[i], w / wt[i]);
for(int k = 0; k <= nCount; k++)
{
    M[i][w] = max(M[i][w], k * value[i] + M[i - 1][w - k * wt[i]]);
}
  • nCount记录的是当前的第i种物品最多可以放多少个,受两个因素限制,第一个因素是一共有A[i]个类型i的物品,第二个因素是背包容量为w,最多能装(w / wt[i])个,取这两个数中较小的一个作为nCount。
  • 接着尝试放0到nCount个类型i的物品到背包。k等于多少就等于放多少个类型i的物品进去。
  • M[i][w]取的是M[i][w]和(k * value[i] + M[i - 1][w - k * wt[i]])中的最大值,这样才能更新M[i][w]。
  • 对于固定的i和w,M[i][w]并不是随着k的增大而增大的,有时候你可以选择不装这么多类型i的物品,留些容量装前面类型的物品,可能会使得总价值更大。

3. 代码实现

#include<iostream>
#include<iomanip> 
using namespace std;
int A[7];
double value[7] = {0, 0.01, 0.05, 0.10, 0.25, 0.50, 1.0};
int wt[7] = {0, 3, 5, 2, 6, 11, 8};
double M[10][11000] = {0};
int main()
{
    int W;
    while(cin >> W)
    {
        A[0] = 0;
        for(int i = 1; i <= 6; i++)
            cin >> A[i];
        for(int w = 0; w <= W; w++)
            M[0][w] = 0;
        for(int i = 0; i <= 6; i++)
            M[i][0] = 0;
        for(int i = 1; i <= 6; i++)
        {   
            for(int w = 1; w <= W; w++)
            {
                M[i][w] = 0;
                int nCount = min(A[i], w / wt[i]);
                for(int k = 0; k <= nCount; k++)
                {
                    M[i][w] = max(M[i][w], k * value[i] + M[i - 1][w - k * wt[i]]); //注意这里是取的是M[i][w]和(k * value[i] + M[i - 1][w - k * wt[i]])中的最大值,而不是M[i - 1][w]和(k * value[i] + M[i - 1][w - k * wt[i]])中的最大值。
                }
            }
        } 
        cout << "$" << fixed << setprecision(2) << M[6][W] << endl;
    }
    return 0;
}                                 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值