这次是一点关于贪心的看法,轻喷QAQ
什么是贪心?
要说贪心算法,骂我们首先就要知道什么是贪心。
通俗一点来讲,我觉得贪心就是我就要最好的;
而站在程序设计的角度的话,贪心就是做出当前情况下最好的选择,也就是局部最优解;这同时也意味着贪心并不一定会得到最优解,但一定是当前的最优解。
那么什么时候适合使用贪心呢?
- 具有最优子结构。把问题划分以后存在最优解(这个最有子结构和动态规划的最优子结构应该是类似的,不过动态规划我还没搞懂,就不瞎逼逼了);
- 最优解的可证明性。也就是说能证明所求问题的最优解存在在子问题里,毕竟我们想要的还是那个最优解。
难受的是,证明最优解的存在不是那么容易,而驱使我们使用贪心的动力就是它的思想容易理解,一但证明最优解存在,那么构造起来比较容易了。感觉好纠结
经典例题
贪心算法比较经典的问题就是背包问题了,这里又可以分成0-1背包问题和部分背包问题。
0-1背包问题就是一个物品要么完全放入背包,要么完全不放入背包,部分背包问题就是物品不用完全放入背包,其中我们可以知道0-1背包问题无法用贪心求出最优解(证明),而部分背包问题是可以求出的,我们只需要按需部分选取单位重量价值最大的物品就能得到最优解。比如:
背包容量50
物品:A B C
重量:25 20 10
价值:30 25 10
它们的单位价值从小到大依次为B-1.25、A-1.2、C-1,背包容量又为40,那么我们只要选取20的B,25的A和5的C就能得到最高价值60。
实例
最后我们来看几个实际问题。
混合牛奶
由于乳制品产业利润很低,所以降低原材料(牛奶)价格就变得十分重要。帮助Marry乳业找到最优的牛奶采购方案。
Marry乳业从一些奶农手中采购牛奶,并且每一位奶农为乳制品加工企业提供的价格是不同的。此外,就像每头奶牛每天只能挤出固定数量的奶,每位奶农每天能提供的牛奶数量是一定的。每天Marry乳业可以从奶农手中采购到小于或者等于奶农最大产量的整数数量的牛奶。
给出Marry乳业每天对牛奶的需求量,还有每位奶农提供的牛奶单价和产量。计算采购足够数量的牛奶所需的最小花费。
直接上代码,不难看懂
#include<iostream>
#include<algorithm> //调用sort(),pair<>
#define maxSize 20000 //不能太大,否则会溢出
using namespace std;
/*
输入格式:
第 1 行共二个数值:N,(0<=N<=2,000,000)是需要牛奶的总数;M,(0<= M<=5,000)是提供牛奶的农民个数。
第 2 到 M+1 行:每行二个整数:Pi 和 Ai。
Pi(0<= Pi<=1,000) 是农民 i 的牛奶的单价。
Ai(0 <= Ai <= 2,000,000)是农民 i 一天能卖给Marry的牛奶制造公司的牛奶数量。
输出格式:
单独的一行包含单独的一个整数,表示Marry的牛奶制造公司拿到所需的牛奶所要的最小费用。
*/
int main(){
int n,m,i;
pair<int,int> milk[maxSize]; //pair<>动态数组,里面有first和second两个参数可调用
cin>>n>>m;
for(i = 0;i < m;i++){ //first是需要牛奶总量,second是提供牛奶户人数
cin>>milk[i].first>>milk[i].second;
}
sort(milk,milk+m);
int price = 0,sum = 0;
//贪心算法,部分
for(i = 0;i < m;i++){
if(sum + milk[i].second <= n){ //牛奶户提供牛奶量不超过需求时
price += milk[i].first*milk[i].second;
sum += milk[i].second;
}
else{
price += milk[i].first*(n-sum);
break;
}
}
cout<<price<<endl; //花费最少
system("pause");
return 0;
}
排队接水
有n个人在一个水龙头前排队接水,假如每个人接水的时间为Ti,请编程找出这n个人排队的一种顺序,使得n个人的平均等待时间最小。
上
#include<iostream>
#include<algorithm>
#include<iomanip> //cout输出浮点数
#define maxSize 1000
using namespace std;
int main(){
int n,i;
double sum = 0.0;
cin>>n;
pair<int,int> queue[maxSize];
for(i = 0;i < n;i++){
cin>>queue[i].first; //first为等待时间
queue[i].second = i+1; //second为序号
}
sort(queue,queue+n); //为使平均等待时间最小,从小到大排序
for(i = 0;i < n;i++){ //计算到第n个人接水时等待时间
sum += queue[i].first*(n-i-1);
cout<<queue[i].second<<" ";
}
cout<<endl;
cout<<fixed<<setprecision(2)<<sum/n; //格式化输出浮点数
system("pause");
return 0;
}