贪心算法及其经典例题

引言:

贪心算法是经典算法之一,也叫贪婪算法或启发式算法,它对整个求解过程的局部做最优调整,它只适用于求较优解或者部分解,而不能求最优解。这样的调整方法叫贪心策略,至于什么问题需要什么样的贪心策略是不确定的,具体问题具体分析。例如求最小生成树的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 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
输入输出:
在这里插入图片描述

扫描二维码关注公众号,回复: 10577246 查看本文章

题解:

要想取得最大的效益,那么如果明天的股票更贵,那么今天就买,如果明天的股票便宜,那么今天就卖,由此可以得到最大效益。

代码:

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;
    }
};
发布了39 篇原创文章 · 获赞 22 · 访问量 1147

猜你喜欢

转载自blog.csdn.net/M_L_Fighting/article/details/105228716
今日推荐