题目描述
阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有 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
思路
这道题就和题目所说的一样,它就是一道背包问题,不过是部分背包问题,不同于01背包问题使用动态规划去解决,部分背包问题主要是使用贪心的思路来解决,部分背包与01背包不同点是物品是可以分割的,不用说一整个物品才能装,一部分也是可以装的,所以我们是一定可以把书包装满的。
接着就解释一下贪心,贪心其实是一种解题思路,它没有固定的解决代码。它是指只考虑局部也就是当前最优而不管这一步是不是全局最优解的一种思路,但是这种只考虑局部的思路却能得到最优解。贪心算法,跟它的名字一样,贪,也就是总是做出在当前看来是最好的选择,而不从当前整体最优上加以考虑,只考虑眼前的利益,有用我们就要。
再说回这道题,我们要求怎么拿才能拿走最大价值的金币,每一堆金币有它的质量和价值,因为我们能那部分的,所以我们是可以将其按比例来算的,每一m占多少v,比例越大,我们就越先选,不管后面的够不够,现在面对的是最大的,我们就选它,直到装不下或者选完了,我们才考虑下一个。这就是我们的贪心思路,先考虑比例大的。
那么就是代码要怎么实现了。
既然我们要比例最大的,那么肯定要先排序,因为我们每个数据既需要记录质量,还需要价值和比例,那么这里我们就构建一个结构体,一次性记录三个。其次就是排序,这里我们直接使用了算法库里面的sort函数,因为sort函数里面是没有对结构体的排序,那么我们就需要自己加上一个mycompare,给sort函数排序。
接着就是进行贪心的思路,如果我们还装得下全部,那就全部装最大的,再去装第二大的;要是装不下,我们就装当前最大的部分,然后就可以结束循环了。
最后就把最大价值给输出即可,不过要注意保留两位小数。
贪心算法主要就是培养这个思路,它没有固定模板,所以需要我们通过写题或者看解析来理解它的思路,得想想怎么考虑局部最优,因为以前的思路绝大多数都是考虑全局最优的,所以需要我们慢慢训练,有时候贪心算法的只考虑局部可以帮助我们解决那些结果受很多影响控制或者很多地方需要考虑的题目。
AC代码
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
using namespace std;
struct bag {
double weight;
double value;
double average;
};
bool mycompany(bag& a, bag& b) {
return a.average > b.average;
}
int main()
{
int n, t;
cin >> n >> t;
vector<bag> pa(n);
for (int i = 0; i < n; i++) {
cin >> pa[i].weight >> pa[i].value;
pa[i].average = 1.0 * (pa[i].value / pa[i].weight);
}
double val = 0;
sort(pa.begin(), pa.end(), mycompany);
for (int i = 0; i < n; i++) {
//cout << pa[i].weight << " " << pa[i].value << endl;
if (t > pa[i].weight) {
t -= pa[i].weight;
val += pa[i].average * pa[i].weight;
}
else {
val += pa[i].average * t;
t = 0;
break;
}
}
cout << fixed << setprecision(2) << val << endl;
return 0;
}