P2240 【深基12.例1】部分背包问题

题目描述

阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有 N(N≤100)堆金币,第 ii堆金币的总重量和总价值分别是 mi,vi(1≤mi,vi≤100)。阿里巴巴有一个承重量为 T(T≤1000)的背包,但并不一定有办法将全部的金币都装进去。他想装走尽可能多价值的金币。所有金币都可以随意分割,分割完的金币重量价值比(也就是单位价格)不变。请问阿里巴巴最多可以拿走多少价值的金币?

输入格式

第一行两个整数 N,T。

接下来 N 行,每行两个整数 mi,vi​。

输出格式

一个实数表示答案,输出两位小数

输入输出样例

输入 #1复制

4 50
10 60
20 100
30 120
15 45

输出 #1复制

240.00

思路分析

首先,由于金币可以任意分割,我们需要根据单位价值(即价值除以重量)来确定优先装入背包的金币。单位价值越高的金币,我们应该优先装入背包,直到背包无法再装入更多金币或所有金币都已装入。

但是,注意到题目中的比较并不是直接比较单位价值(因为直接比较可能会遇到浮点数的精度问题),而是通过比较两个金币的v/mm/v的倒数关系(即v*b.m > b.v*a.m)来间接实现按单位价值从高到低排序。这种比较方式在整数运算中更为稳定,避免了浮点数比较可能带来的问题。

接下来,我们遍历排序后的金币列表,尝试将金币装入背包。如果当前金币可以完全装入背包(即背包剩余容量大于等于当前金币重量),则我们直接将该金币全部装入背包,并更新背包剩余容量和总价值。如果当前金币不能完全装入背包,我们则按照当前金币的单位价值(即完整金币的价值)将其加入总价值,并更新背包剩余容量。这个过程一直持续到背包无法再装入任何金币或所有金币都已尝试装入。

示例代码

#include<bits/stdc++.h>  
using namespace std;  
  
// 定义金币的结构体,包含重量和价值  
struct Money {  
    double m, v;  
};  
  
// 比较函数,用于比较两个金币的单位价值(通过比较v*b.m和b.v*a.m实现)  
int cmp(Money a, Money b) {  
    return a.v * b.m > b.v * a.m;  
}  
  
int main() {  
    int n, t; // n为金币堆数,t为背包承重量  
    cin >> n >> t;  
      
    Money money[101]; // 定义一个足够大的数组来存储金币信息  
    for (int i = 1; i <= n; i++) {  
        cin >> money[i].m >> money[i].v;  
    }  
      
    // 对金币按单位价值从高到低排序  
    sort(money + 1, money + 1 + n, cmp);  
      
    double ans = 0; // 初始化总价值为0  
    for (int i = 1; i <= n; i++) {  
        // 如果背包剩余容量大于等于当前金币重量,则完全装入背包  
        if (t >= money[i].m) {  
            ans += money[i].v; // 加入总价值  
            t -= money[i].m; // 更新背包剩余容量  
        } else {  
            // 如果背包剩余容量小于当前金币重量,则按单位价值装入部分金币  
            ans += money[i].v / money[i].m * t; // 计算部分金币的价值  
            t = 0; // 背包已满,剩余容量设为0  
            break; // 跳出循环,因为背包已满  
        }  
    }  
      
    // 输出结果,保留两位小数  
    printf("%.2lf", ans);  
      
    return 0;  
}

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值