贪心算法——思路与例题

贪心算法:当我们分析一个问题时,我们往往先以最优的方式来解决问题,所以顾名思义为贪心。

例题1

题目分析:这题利用贪心算法来分析,最优解(可容纳人数最多时)一定是先考虑六人桌,然后再考虑四人桌。

1:六人桌

  • 把两个三人寝安排到六人桌(可容纳6人)
  • 其次就是把一个二人寝安排到六人桌,这样子还多出了一个四人桌(可容纳2人,但是多了一张四人桌)
  • 再考虑把一个四人寝安排到六人桌(可容纳四人)
  • 最后是把三人寝安排到六人桌(可容纳三人)

2:四人桌

  • 最优解就是把四人寝安排到四人桌(可容纳四人)
  • 其次是把两个二人寝安排到四人桌(可容纳四人)
  • 把一个三人寝安排到四人桌(可容纳三人)
  • 把一个二人寝安排到四人桌(可容纳两人)

以上就是最优解的排序过程,我们只要按照这个顺序来安排位置,那么可以容纳的人数一定是最多的。

代码实现:

cpp 复制代码
#include <iostream>
using namespace std;

inline void  solve()
{
	int a2, a3, a4, b4, b6,ct=0;
	cin>>a2>>a3>>a4>>b4>>b6;
	/*先考虑六人桌*/
	while (a3 >= 2 && b6)/*有两个三人寝,且有六人桌时*/
	{
		ct += 6; a3 -= 2; b6 --;
	}
	while (a2 && b6)/*有一个二人寝,且有六人桌时*/
	{
		ct += 2; a2--; b6--; b4++;
	}
	while (a4 && b6)/*有一个四人寝,且有六人桌时*/
	{
		ct += 4; a4--; b6--;
	}
	while (a3 && b6)/*有一个三人寝,且有六人桌时*/
	{
		ct += 3; a3--; b6--;
	}
	/*再考虑四人桌*/
	while (a4 && b4)/*当有四人寝,且有四人桌时*/
	{
		ct += 4; a4--; b4--;
	}
	while (a2 >= 2 && b4)/*当有两个二人寝,且有四人桌时*/
	{
		ct += 4; a2 -= 2; b4--;
	}
	while (a3 && b4)/*当有三人寝,且有四人桌时*/
	{
		ct += 3; a3--; b4--;
	}
	while (a2 && b4)/*当有二人人寝,且有四人桌时*/
	{
		ct += 2; a2--; b4--;
	}
	cout << ct << endl;
}
int main()
{
	int n;
	cin >>n;
	while (n--)
	{
		solve();
	}
	return 0;
}

例题2(巧克力)

题目分析:现在一共有n种巧克力,每种巧克力包含三个信息:价格,保质期,数量。现在要买能吃x天的巧克力(一天吃一块),题目要求花的钱最少。现在思考有没有一种最优路径可以达到我们的目的如果有,那么就可以使用贪心算法。

再进一步分析,在保质期没过的前提下,我们能选择的巧克力种类越多越好,这样我们就可以在这些种类里选择价格最低的。那么我们应该倒序考虑,这样我们可以选择的巧克力种类才最多,因为在第x天时,我们选择的巧克力的保质期一定是大于等于x的,这就意味着,当在1---x天里的任意一天时,可以选择的巧克力种类最多。

代码实现:

cpp 复制代码
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
int x, n;
const int N = 1e5 + 5;

struct Q 
{
	int id, value;
	bool operator< (const Q& rhs) const { return value > rhs.value; }/*重载为小到大*/
};
priority_queue <Q> q;/*大堆根.价格*/
struct Node 
{
	int price, date, amount;
	bool operator<(const Node& rhs)const { return date > rhs.date; }/*重载<为降序*/

}cho[N];
int main()
{
	ios::sync_with_stdio(false);
	int sum = 0,st=1;
	cin >> x >> n;
	for (int i = 1; i <= n; i++)
	{
		cin>>cho[i].price>> cho[i].date >> cho[i].amount;
	}
	sort(cho + 1,cho+1+n);/*按照保质期从大到小排序*/
	int pos=1;
	for (int i = x; i >= 1; i--)/*当在第i天时*/
	{
		
		while (pos <= n && cho[pos].date >= i)/*把满足条件的巧克力种类都列入考虑范围*/
		{
			cho[pos].amount--;
			q.push({ pos, cho[pos].price });/*队列中最多有n组数据,且按照价格升序*/
			pos++;
		}
		if (q.empty())
		{
			st = 0;
				break;
		}
		Q now = q.top();/*得到价格最低的巧克力信息*/
		sum += now.value;
		if (cho[now.id].amount)/*如果这种巧克力还有,继续列入考虑范围*/
		{
			cho[now.id].amount--;
			q.push({now.id,cho[now.id].price});
		}
		q.pop();/*选择吃掉价格最低的巧克力*/
		
	}
	if (st == 0)cout << "-1" << endl;
	else cout << sum;
	cout << sum;
	return 0;
}

代码分析:使用优先队列来实现对价格的升序排列。优先队列中只存放巧克力的种类和价格。以方便我们选择价格最低的巧克力,种类是用来找到这种价格最低的巧克力的数量的,所以不可或缺。

我们先按照保质期对巧克力进行降序排序(大的在前,小的在后)。然后在选择出可以吃的巧克力类别(一共有n种类别),当我把某种巧克力列入考虑范围之后(放到优先队列中),这种巧克力的数量要减一。当完成判断后,此时优先队列中就是我们在第i天时总共可以选择的巧克力了,因为我们优先队列我们按照价格升序排列的,所以取出队头(价格最低)信息之后,判断这种巧克力是否还有,有的话再存入到优先队列q之中,还要把对头对应的种类数量减一。最后要弹出队头,表示吃了这块巧克力。如果在遍历的过程中出现了队列为空的情况,说明没有解决方案。

例题3(三国游戏)

题目分析:要想知道当某个国家获胜时最多有多少个事件发生,我们首先思考有没有某种最优路径能判断出某个国家胜利时的发生的最多事件,然后取出这三个国家发生的事件的最大值。这里主要的困难就是获胜条件:a>b+c如何转变,如何达到最简判断出这个国家获胜时最多能发生的事件。

数据转变:把第i个事件三个独立的值转化为一个值,但是这个值包含了这三个独立数据的信息,我们通过判断这个值的大小,就可以知道这个国家有没有获胜。那么如何实现呢,具体就是对于第一个国家,那么这个值就是a-b-c,对于第二个国家,这个值就是b-a-c,对于第三个国家,这个值就是c-a-b,可以看出,其实就是相对值,也就是这个事件发生时,对第一个国家,第二个国家,第三个国家的胜利的贡献值。贡献值越大,说明这个事件发生对某个国家胜利越有利。所以问题就迎刃而解了,我们把复杂的三个数据简化为了一个数据,只需要对这个数据进行降序排序,先发生贡献值大的事件,先加正的,再加负的。循环计算求和,当和小于零时,说明这个国家已经失败了。这样我们就判断出了最多发生的事件数量。然后比较这三个国家的事件数量,取最大值即可。

cpp 复制代码
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
int x, n;
const int N = 1e5 + 5;

struct abc 
{
	long long int a, b, c,pure_a,pure_b,pure_c;

}ABC_i[N];
bool max_a(const struct abc &s1, const struct abc& s2)
{
	return s1.pure_a >s2.pure_a;
}
bool  max_b(const struct abc& s1, const struct abc& s2)
{
	return s1.pure_b > s2.pure_b;
}
bool  max_c(const struct abc& s1, const struct abc& s2)
{
	return s1.pure_c > s2.pure_c;
}
int  win(int x)
{
	int ct=0,sum=0;
	if (x == 1)
	{
		
		sort(ABC_i+1,ABC_i+1+n, max_a);/*判断X国*/
		for (int i = 1; i <= n; i++)
		{
			if (sum + ABC_i[i].pure_a > 0)
			{
				sum+= ABC_i[i].pure_a;
					ct++;
			}
			else break;
		}
		
	}
	if (x == 2)
	{

		sort(ABC_i + 1, ABC_i + 1 + n, max_b);/*判断Y国*/
		for (int i = 1; i <= n; i++)
		{
			if (sum + ABC_i[i].pure_b > 0)
			{
				sum += ABC_i[i].pure_b;
				ct++;
			}
			else break;
		}

	}
	if (x == 3)
	{

		sort(ABC_i + 1, ABC_i + 1 + n, max_c);/*判断Z国*/
		for (int i = 1; i <= n; i++)
		{
			if (sum + ABC_i[i].pure_c > 0)
			{
				sum += ABC_i[i].pure_c;
				ct++;
			}
			else break;
		}

	}
	if (ct == 0) ct = -1;
	return ct;
}
int main()
{
	int answer=-1,x,y,z;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> ABC_i[i].a;
	}
	for (int i = 1; i <= n; i++)
	{
		cin >> ABC_i[i].b;
	}
	for (int i = 1; i <= n; i++)
	{
		cin >> ABC_i[i].c;
	}
	for (int i = 1; i <= n; i++)
	{
		
		x = ABC_i[i].a - ABC_i[i].b - ABC_i[i].c;
		ABC_i[i].pure_a = x;

		y = ABC_i[i].b - ABC_i[i].a - ABC_i[i].c;
		ABC_i[i].pure_b = y;

		z = ABC_i[i].c - ABC_i[i].b - ABC_i[i].a;
		ABC_i[i].pure_c = z;
	}
	
	/*for (int i = 1; i <= n; i++)
	{
		cout << ABC_i[i].pure_a << " " << ABC_i[i].pure_b << " " << ABC_i[i].pure_c << endl;;
	}*/
	for (int i = 1; i <= 3; i++)
	{
		answer=max(win(i),answer);
	}
	cout << endl<<endl<< " " << answer;
	return 0;
}
相关推荐
MobiCetus14 分钟前
如何一键安装所有Python项目的依赖!
开发语言·jvm·c++·人工智能·python·算法·机器学习
思麟呀17 分钟前
String类的模拟实现
开发语言·c++·算法
Dante79841 分钟前
判断质数及其优化方法
开发语言·c++·算法
ylfhpy1 小时前
Java面试黄金宝典19
java·开发语言·数据结构·算法·面试·面经
不知名。。。。。。。。1 小时前
C++———— Vector
c++·算法·vector
姜威鱼1 小时前
蓝桥杯python编程每日刷题 day 20
数据结构·算法·蓝桥杯
longlong int2 小时前
【每日算法】Day 9-1:贪心算法精讲——区间调度与最优选择(C++实现)
算法·贪心算法
张琪杭2 小时前
python算法:leetcode二叉树相关算法题
python·算法·leetcode·职场和发展
LIUJH12332 小时前
哈希冲突 及 双哈希
开发语言·数据结构·c++·算法·哈希算法
念九_ysl2 小时前
暴力搜索算法详解与TypeScript实战
javascript·算法