引言:
贪心算法是经典算法之一,也叫贪婪算法或启发式算法,它对整个求解过程的局部做最优调整,它只适用于求较优解或者部分解,而不能求最优解。这样的调整方法叫贪心策略,至于什么问题需要什么样的贪心策略是不确定的,具体问题具体分析。例如求最小生成树的Prim算法和Kruskal算法都是漂亮的贪心算法。
贪心算法适用的问题:
背包问题、带有期限的作业排序,最小生成树、单源点最短路径
贪心算法的正确性:
要想证明贪心算法的正确性,那么就要证明出贪心解就等于最优解。大多数很容易证明出贪心解等于最优解。证明贪心解等于最优解的方法有:数学归纳法、反证法等等。
问题描述:
有一类问题,它有n个输入,而它的解就是这n个解的某个子集,这些子集必须满足某些先给定的条件。贪心方法是指在对问题求解时,总是做出在当前看来是最好的选择。通过选择局部最优已到达或接近全局最优。
贪心算法的相关题目:
题目1:合并果子
题目链接
题目描述:
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n-1n−1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 11 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有 33 种果子,数目依次为 11 , 22 , 99 。可以先将 11 、 22 堆合并,新堆数目为 33 ,耗费体力为 33 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 1212 ,耗费体力为 1212 。所以多多总共耗费体力 =3+12=15=3+12=15 。可以证明 1515 为最小的体力耗费值。
输入输出示例:
题解:
这道题很明显就可以使用贪心算法求解。每次选择两个最小的果子堆进行合并,从而达到消耗的体力最小。如果选择较大的果子堆进行合并,则合并后的果子堆更大,对于以后的合并产生不利会逐渐增加。所以肯定不会是最优解。
代码:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
int n;
cin>>n;
vector<int> ve(n);
int i;
for(i=0;i<n;i++)
cin>>ve[i];
int sum=0;
sort(ve.begin(),ve.end());
for(i=0;i<n-1;i++)
{
ve[i+1]+=ve[i];//找到前面两个最小的
sum+=ve[i+1];
int j=i+2,temp=ve[i+1];
while(j<n&&temp>ve[j])//把合并后的值插入,使序列递增
{
ve[j-1]=ve[j];
j++;
}
ve[j-1]=temp;
}
cout<<sum<<endl;
return 0;
}
题目2:部分背包问题
题目链接
题目描述:
输入输出:
题解:
因为金币可以任意分割,那么要想拿走价值最大的金币,就要去单位重量价值最大的金币。所以我们只需对单位重量价值最大的金币进行排序,直到取出的金币装满整个背包。
代码:
#include<iostream>
#include<algorithm>
#include<vector>
#include<iomanip>
using namespace std;
struct coin
{
int wealth;
int weight;
double avg;
};
bool cmp(coin a,coin b)
{
return a.avg>b.avg;
}
int main()
{
vector<coin> ve;
int a,b,i,n,sum;
cin>>n>>sum;
for(i=0;i<n;i++)
{
cin>>b>>a;
coin s;
s.wealth=a;
s.weight=b;
s.avg=a*1.0/b;
ve.push_back(s);
}
sort(ve.begin(),ve.end(),cmp);
double money=0;
for(i=0;i<ve.size();i++)
{
if(sum>=ve[i].weight)
{
money+=ve[i].wealth;
sum-=ve[i].weight;
}
else
{
money+=ve[i].avg*sum;
break;
}
}
cout<<setiosflags(ios::fixed)<<setprecision(2)<<money<<endl;
return 0;
}
题目3:买卖股票的最佳时期
题目链接
题目描述:
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
输入输出:
题解:
要想取得最大的效益,那么如果明天的股票更贵,那么今天就买,如果明天的股票便宜,那么今天就卖,由此可以得到最大效益。
代码:
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size()==0)
{
return 0;
}
int i,j;
int sum=0;
for(i=0;i<prices.size()-1;i++)
{
if(prices[i+1]>prices[i])
{
j=i+1;
while(j+1<prices.size()&&prices[j+1]>prices[j])
{
j++;
}
sum+=prices[j]-prices[i];
i=j;
}
}
return sum;
}
};