P2240 例题代码分析

索引

#部分背包问题 #贪心算法

题目

题目来源 - https://www.luogu.com.cn/problem/P2240

深基12.例1 部分背包问题
题目描述
阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有 N ( N ≤ 100 ) N(N \le 100) N(N100) 堆金币,第 i i i 堆金币的总重量和总价值分别是 m i , v i ( 1 ≤ m i , v i ≤ 100 ) m_i,v_i(1\le m_i,v_i \le 100) mi,vi(1mi,vi100)。阿里巴巴有一个承重量为 T ( T ≤ 1000 ) T(T \le 1000) T(T1000) 的背包,但并不一定有办法将全部的金币都装进去。他想装走尽可能多价值的金币。所有金币都可以随意分割,分割完的金币重量价值比(也就是单位价格)不变。请问阿里巴巴最多可以拿走多少价值的金币?
输入格式
第一行两个整数 N , T N,T N,T
接下来 N N N 行,每行两个整数 m i , v i m_i,v_i mi,vi
输出格式
一个实数表示答案,输出两位小数
样例 #1
样例输入 #1

4 50
10 60
20 100
30 120
15 45

样例输出 #1

240.00

代码

分析《深入浅出程序设计竞赛(基础篇)》

#include<cstdio>
#include<algorithm>
using namespace std;
struct coin{ // 结构体
	int m,v; // 整型变量成员m,v 堆中金币的总重量和总价值
} a[110];
bool cmp(coin x,coin y){// 自定义函数 比较两堆金币的单位重量的价值
	return x.v*y.m>y.v*x.m; // x堆的价值 * y堆的重量 > y堆的价值 * x堆的重量  》 v1/m1 > v2/m2 》 v1*m2 > v2*m1
}
int main(){
	int n,t,c,i;
	float ans =0;
	scanf("%d%d",&n,&t); // 读入整型数据到n和t  n堆金币 t重量容量的背包
	c=t;// c等于背包的容量
	for(i=0;i<n;i++){ // i》 0,1,2,...,n-1  》 从0开始遍历每一堆金币的数据
		scanf("%d%d",&a[i].m,&a[i].v); // 读入整型数据重量,价值到 a[i] 堆中的m,v
	}
	sort(a,a+n,cmp);// 按cmp函数的规则(单位价值)排列金币堆
	for(i=0;i<n;i++){// 按总堆数进行遍历 0..n
		if(a[i].m>c){// 如果i堆的重量大于c则退出遍历
			break;
		}
		c-=a[i].m; // 从c中减去i堆的重量
		ans+=a[i].v;// ans中增加i堆的价值
	}
	if(i<n){// 如果没有遍历到最后一堆
		ans+=1.0*c/a[i].m*a[i].v;// 1*c/im*iv 》 剩余容积/ i堆重量*i堆价值 》 容积* i堆价值/i堆重量 》容积*i堆单价
		// 在ans中增加从i堆中取出放满背包的金币的价值。
	}
	printf("%.2lf",ans); // 输出背包中的金币的总价值
	return 0;
}
/*
算法分析
1. 将金币堆按价值排序
2. 从单位重量价值最大的金币堆开始装包,直到整堆装不进去为止
3. 判断是否将所有金币都装包了,
3.1. 如果没有将未装包的第一堆(剩余单价最大的)中剩余背包容积的部分装包
4. 输出背包中金币的总价值

PS: 明显这个金币不是纯金。。。。 
*/
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值