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

题目描述

阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有 N(N≤100) 堆金币,第 i 堆金币的总重量和总价值分别是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

-------------------------------------------------------华丽的分割线--------------------------------------------------------

这题的题意我就不多说了。我先点名它的思路:

贪心算法。典型的分割背包问题,很多题目多是它转化的。它和动态规划背包问题不同的是他的物品能分割,就是把物品是放入背包时价值和重量一起除以同一个数。所以代码同学们一定要记住。

-------------------------------------------------------华丽的分割线--------------------------------------------------------

这题同样是贪心。那我们的贪心策略是优先选性价比高的物品。性价比就是指物品的单位价格。列如重量是3, 价格是12,那他的性价比就是12/3=4。到最后放不下并且还可以载一点重的时候我们就把物品进行分割。这样我们每次把重量小,价值高的物品放进背包,这就是最好的方案。

-------------------------------------------------------华丽的分割线--------------------------------------------------------

我的代码定义了一个结构体,w代表重量,c代表价值,xjb代表性价比。接下来对性价比从大到小进行很难很难写的懒人专用的sort排序。然后定义了z和ans, 分别代表剩余载重量和总价值数。然后for循环对性价比从大到小进行遍历处理。if判断了是否能将物品整个一起装进书包里。如果能,z减去物品的总体重量,并把价值加到ans里面。否则就对物品进行分割。用物品的性价比,也就是单位价值去乘以剩下载重量并统计进ans中。怎么理解这条公式呢?单位价值也就是当重量为1时物品的价格,在这条公式中z就是单位,物品的价值就是价值,那乘起来就是单位价值了。是不是很好理解。接着用break退出循环,毕竟你都要分割了,背包载重量没用完那就有鬼了。

别忘了单位价值是要用小数的哦!所以我struct里面都是小数。还有输出要保留两位小数。当然,我这个代码有个bug,就是当性价比小数点之后有很多位时排序中的比较会出现多位小数比较高精度出错的问题。举个例子:a=3.33333333333, b=3.33333333334时,他们两直接进行比较的结果很可能会相等。改这个bug只需要改成x.c*y.w>w.x*y.c, 这条公式用到了分数交叉相乘原理。就是用第一个数的分子乘以第二个数的分母和第一个数的分母乘以第二个数的分子进行比较。但是本题的测试样例弱爆了不需要用到,我这个勤奋的学生懒人也懒得写。

-------------------------------------------------------华丽的分割线--------------------------------------------------------

接下来时撒花代码。

#include <bits/stdc++.h>
using namespace std;
struct node{
	double w, c, xjb;
}a[105];
bool cmp(node x, node y){
	return x.xjb>y.xjb;
}
int main() {
	int n;
	double z, ans=0.0;
	scanf("%d%lf", &n, &z);
	for(int i=0; i<n; i++){
		scanf("%lf%lf", &a[i].w, &a[i].c);
		a[i].xjb=a[i].c/a[i].w;	
	}
	sort(a, a+n, cmp);   
	for(int i=0; i<n; i++){
		if(a[i].w<=z){
			z-=a[i].w;
			ans+=a[i].c;
		}else{
			ans+=a[i].xjb*z;
			break;
		}
	}             
	printf("%.2f", ans);
	return 0;                                                                                     
}

最后,希望大家能给我关注和赞, 本弱鸡博主会非常感谢你们。

还有,关注我的人我都关注回去了,不信你们就关注一下试试,粉丝又会噌噌噌的往上增的。

谢谢大家。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值