算法模板:经典贪心问题

前言

⭐️感谢相遇,唤我沈七就好。
⭐️如果能和你一起进步那就太好啦。

什么是贪心

贪心的本质是选择每一阶段的局部最优,从而达到全局最优

这么说有点抽象,来举一个例子:

例如,有一堆钞票,你可以拿走十张,如果想达到最大的金额,你要怎么拿?

指定每次拿最大的,最终结果就是拿走最大数额的钱。

每次拿最大的就是局部最优,最后拿走最大数额的钱就是推出全局最优。

这便是 “贪” 字的由来,考虑局部最优解便是贪心思想的精髓。

其实 此次有一点 标题 党的嫌疑了,因为贪心问题并没有一个通用的模板。

大多数的贪心思想 其实很容易想出来,但难的是证明这样做法的正确性,
所以建议读者做贪心问题时,不要从大量时间 来推理解法的正确性,可以换种思路,尝试举出 与解法相悖的反例,如果列举不出反例,则解法应该是没问题的。

区间问题

1.按左端点或右端点排序

2.寻找性质

区间选点

给定 N 个闭区间 [a i,b i],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。

输出选择的点的最小数量。位于区间端点上的点也算作区间内。

输入格式

第一行包含整数 N,表示区间数。接下来 N

行,每行包含两个整数 a i,b i,表示一个区间的两个端点。

输出格式

输出一个整数,表示所需的点的最小数量。

数据范围

1≤N≤105
−109≤a ib i≤109

输入样例:

3
-1 1
2 4
3 5

输出样例:

2

题解部分:

贪心思路

1.先将每个区间按右端点从小到大排序

2.然后每次枚举每个区间,当某个区间内已经取了点时,直接跳过即可,否则取每个区间最右端的端点。

image_19.png

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
struct PII{
    
    
	int l,r;
}s[N];
bool cmp(PII a,PII b)
{
    
    
	return a.r<b.r;
}
int main()
{
    
    
	int n;
	cin>>n;
	for(int i = 0 ; i < n ; i ++)
	cin>>s[i].l>>s[i].r;
	sort(s,s+n,cmp);
	int ans = 0, ed= -2e9+10;
	for(int i = 0 ; i < n ; i ++)
	if(ed<s[i].l)
	{
    
    
		ed=s[i].r;
		ans++;
	}
	cout<<ans;
	return 0;
}

区间数量

给定 N 个闭区间 [a i,b i],请你在数轴上选择若干区间,使得选中的区间之间互不相交(包括端点)。

输出可选取区间的最大数量。

输出格式

输出一个整数,表示可选取区间的最大数量。

数据范围

1≤N≤105,
−109≤a ib i≤109

输入样例:

3
-1 1
2 4
3 5

输出样例:

2

题解部分:

和上一题 可以说是完全一样了

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
struct PII{
    
    
	int l,r;
}s[N];
bool cmp(PII a,PII b)
{
    
    
	return a.r<b.r;
}
int main()
{
    
    
	int n;
	cin>>n;
	for(int i = 0 ; i < n ; i ++)
	cin>>s[i].l>>s[i].r;
	sort(s,s+n,cmp);
	int ans = 0, ed= -2e9+10;
	for(int i = 0 ; i < n ; i ++)
	if(ed<s[i].l)
	{
    
    
		ed=s[i].r;
		ans++;
	}
	cout<<ans;
	return 0;
}

排队接水

n 个人排队到 1 个水龙头处打水,第 i 个人装满水桶所需的时间是 t i,请问如何安排他们的打水顺序才能使所有人的等待时间之和最小 ?

输入样例:

7
3 6 1 4 2 5 7

输出样例:

56

数据范围

1≤n≤10 ^5
1≤t i≤10^4

题解部分:

坑点1 :此题让求解的是等待时间,不算自己接水的时间

坑点2:所有人的等待时间之和,所以需要求解每一个人的等待时间后再累加一遍

坑点3:虽然题目范围 是 10^4 但经过10^5的累加 必然 会爆int 所以需要用 long long

贪心思想:

按等待时间 从小到大排序,用时少的先接水

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
long long s[N],sum,ans;
int main()
{
    
    
	int n,m;
	
	cin>>n;
	
	for(int i = 0 ;i < n ; i ++)
	cin>>s[i];
	sort(s,s+n);
	for(int i = 0 ;i < n - 1; i ++)
	{
    
    
		sum+=s[i];
		ans+=sum;
	}
	cout<<ans;
	return 0;
}

耍杂技的牛

奶牛们正在试图找到自己在这个堆叠中应该所处的位置顺序。

N 头奶牛中的每一头都有着自己的重量 W i 以及自己的强壮程度 S i

一头牛支撑不住的可能性取决于它头上所有牛的总重量(不包括它自己)减去它的身体强壮程度的值,

现在称该数值为风险值,风险值越大,这只牛撑不住的可能性越高。

您的任务是确定奶牛的排序,使得所有奶牛的风险值中的最大值尽可能的小。

输入格式

第一行输入整数 N,表示奶牛数量。接下来 N行,

每行输入两个整数,表示牛的重量和强壮程度,

i 行表示第 i 头牛的重量 W i 以及它的强壮程度 S i

输出格式

输出一个整数,表示最大风险值的最小可能值。

数据范围

1≤N≤50000
1≤W i≤10,000,
1≤S i≤1,000,000,000

输入样例:

3
10 3
2 5
3 3

输出样例:

2

题解部分:

贪心思路:

我们先分析每头牛的危险值 = 他前面牛的w(重量值)和 - 自身的s(强壮值),

要使每头牛的危险值最小,这显然是与w 和 s同时相关,

所以猜测出一种做法按 每头牛的w + s进行升序排序

然后依次从小到达 按层数由高到低的顺序来计算每头牛的危险值

#include <algorithm>
#include <iostream>
#define x first
#define y second
using namespace std;

typedef pair<int, int> pii;

const int N = 5e4 + 10;

int n;
pii a[N];

int main()
{
    
    
    cin >> n;
    for (int i = 0; i < n; i++) {
    
    
        int w, s;
        cin >> w >> s;
        a[i] = {
    
    w + s, w};
    }

    sort(a, a + n);
    int res = -1e9;
    for (int i = 0, sum = 0; i < n; i++) {
    
    
        int w = a[i].y, s = a[i].x - w;
        res = max(res, sum - s);
        sum += w;
    }

    cout << res << endl;
    return 0;
}

完结散花

ok以上就是对 经典贪心问题 的全部讲解啦,很感谢你能看到这儿。如果有遗漏、错误或者有更加通俗易懂的讲解,欢迎小伙伴私信我,我后期再补充完善。

参考文献

https://www.acwing.com/activity/content/19/

猜你喜欢

转载自blog.csdn.net/m0_66139206/article/details/124201624
今日推荐