acwing算法基础课-第六章 贪心 第七章 时间复杂度

贪心

贪心跟动态规划一样,没有模板,但比动态规划更狠的是,贪心没有套路。贪心一般很难,我们从小到大的考试当中,不可能在考场上去解决一个从来没有遇到过的问题,一般都是把某个问题转化为我们以前见过的某一类问题。不会去解决一个全新的问题。
考试一般是考查转化的能力,所以把所有的模型背下来其实也是可以的。

区间问题

贪心问题,区间问题无外乎就是排序

  1. 按左端点排序
  2. 按右端点排序
  3. 双关键字排序(先按右端点,再按左端点)

没有思路就先试一下,举一些例子,感受一下是不是有问题,看看有什么规律没有。

AcWing 905 区间选点

题目解析转载:
糖豆爸爸

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;

struct Range{
	int l;
	int r;
	bool operator< (const Range &W)const
	{
		return r < W.r;
	}
}range[N];

int main()
{
	int n;
	cin >> n;
	for(int i = 0; i < n; i++)
	cin >> range[i].l >> range[i].r;
	
	sort(range, range + n);
	
	int res = 0;
	int ed = -2e9;
	for(int i = 0; i < n; i++)
	{
		if(range[i].l > ed)
		{
			res++;
			ed = range[i].r;
		}
	 } 
	
	cout << res << endl;
	
	
	return 0;
 } 

AcWing 908 最大不相交的区间数量

代码跟上一题完全一样
题目解析:糖豆爸爸

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;

struct Range{
	int l, r;
	bool operator< (const Range &W)const
	{
		return r < W.r;
	}
}range[N];

int main()
{
	int n;
	cin >> n;
	for(int i = 0; i < n; i++)
	cin >> range[i].l >> range[i].r;
	
	sort(range, range + n);
	
	int res = 0;
	int ed = -2e9;
	for(int i = 0; i < n; i++)
	if(range[i].l > ed)
	{
		res++;
		ed = range[i].r;
	 } 
	
	cout << res << endl;
	return 0;
 } 

AcWing 906 区间分组

该代码不同于前面两个题,该题是按左端点排序。

什么题用什么排序,还是得具体情况具体分析。先分析,找思路,再尝试。
题目解析:糖豆爸爸

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 100010;

struct Range{
	int l;
	int r;
	bool operator< (const Range &W)const
	{
		return l < W.l;
	}
}range[N];

int main()
{
	int n;
	cin >> n;
	for(int i = 0; i < n; i++)
	cin >> range[i].l >> range[i].r;
	
	sort(range, range + n);
	
	priority_queue<int, vector<int>, greater<int> > heap;
	for(int i = 0; i < n; i++)
	{
		if(heap.empty() || range[i].l <= heap.top() ) 
			heap.push(range[i].r);
		else
		{
			heap.pop();
			heap.push(range[i].r);
		}
	}
	
	cout << heap.size() << endl;
	return 0;
 } 

AcWing 907 区间覆盖

题目解析:糖豆爸爸

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010; 

struct Range{
	int l , r;
	bool operator< (const Range &W)const
	{
		return l < W.l; 
	 } 
}range[N];

int main()
{
	int st, ed;
	cin >> st >> ed;
	int n;
	cin >> n;
	
	for(int i = 0; i < n; i++)
	cin >> range[i].l >> range[i].r;
	 
	sort(range, range + n);
	
	int res = 0;
	bool success = false;
	
	for(int i = 0 ; i < n; i++)
	{
		int j = i;
		int r = -2e9;
		//能覆盖st且右端点最大的区间 
		while(j < n && range[j].l <= st)
		{
			r = max(r, range[j].r);
			j++;
		}
		
		//如果没有区间能覆盖st(两种情况)
		// 1. 有区间 l < st, 但 r 也 < st 
		// 2. 没有区间 l < st, 即 r 没有改变,为 -2e9  
		if(r < st)
		{
			res = -1;
			break;
		}
		
		res++;
		if(r >= ed)
		{
			success = true;
			break;
		}
		//更新 
		st = r;
		i = j - 1;
	}
	
	if(success) cout << res << endl;
	else cout << "-1" << endl;
	return 0;
 } 

Huffman树

AcWing 148. 合并果子

题目解析:糖豆爸爸

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

int main()
{
	int n;
	cin >> n;
	priority_queue<int, vector<int>, greater<int> > heap;
	
	for(int i = 0; i < n; i++)
	{
		int x;
		cin >> x;
		heap.push(x);	
	}
	
	int res = 0;
	while(heap.size() != 1)
	{
		int a = heap.top(); heap.pop();
		int b = heap.top(); heap.pop();
		heap.push(a + b);
		res += (a + b);
	}
	
	cout << res << endl;
	return 0;
}

排序不等式

AcWing 913. 排序打水

思路: 让最慢的人最后打水,谁快就谁先来。

在这里插入图片描述

#include<iostream>
#include<algorithm>
using namespace std;

typedef long long LL;

const int N = 100010;
int a[N];
int main()
{
	int n;
	cin >> n;
	for(int i = 0; i < n; i++) cin >> a[i];
	
	sort(a, a + n);
	
	LL res = 0;
	for(int i = 0; i < n; i++)
	res += a[i] * (n - i - 1);
	
	cout << res << endl;
	return 0;
 } 

绝对值不等式

AcWing 104 货仓选址

在这里插入图片描述

上图来自
Cloudeeeee

在这里插入图片描述
注意上图的 >=
将第二个右边等式 抽象为 (|a - x| + |b - x|),
当 x 位于所有情况的 a 与 b 之间时,>= 为 =,
(当 x 位于 某个 a的左边,或 b 的右边,则(|a - x| + |b - x|)> b - a,即 >= 的 = 不成立)

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int a[N];

int main()
{
	int n;
	cin >> n;
	for(int i = 0; i < n; i++) cin >> a[i];
	
	sort(a, a + n);
	
	int res = 0;
	for(int i = 0; i < n; i++)
		res += abs(a[i] - a[n / 2]);
		
	cout << res << endl;
	
	return 0;
 } 

推公式

AcWing 125 耍杂技的牛

解释公式

以下图片来自www.acwing.com算法基础课

如果 wi + si > w(i + 1) + s(i +1),(即有两个不符合要求)
我们只交换第 i 个位置和第 i + 1 个位置的牛,对于其他牛的危险值是不变的。

在这里插入图片描述
关于右上角的转换

在这里插入图片描述
2. 因为我们只交换第 i 个位置和第 i + 1 个位置的牛,对于其他牛的危险值是不变的。则,
在这里插入图片描述
3. 各项加上 s(i) + s(i + 1), 大小关系不变
在这里插入图片描述
好,s(i) + s(i + 1), w(i) + w(i + 1)都是整数,

wi + si > si;
wi + si > w(i + 1) + s(i + 1)(已经假设了)
那么不管 s(i + 1)为何值,我们可以知道
max( s(i + 1),(wi + si )) > max( s(i),(w(i + 1) + s(i + 1) ))
也就是说交换前的 max值大于交换后的 max值,但显然,我们想要是更小的 max值,即交换后的 max值,这说明交换后是更优的,
即 w(i + 1) + s(i + 1) > wi + si

所以 按 wi + si 从小到大的顺序排,最大的危险系数一定是最小的

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 50010;
typedef pair<int, int> PII;
PII cow[N];

int main()
{
	int n;
	cin >> n;
	for(int i = 0; i < n; i++)
	{
		int w, s;
		cin >> w >>s;
		cow[i] = {w + s, s}; // w + s 是为了排序用 
	}
	
	sort(cow, cow + n);
	
	int res = -2e9;
	int num = 0;
	for(int i = 0; i < n; i++)
	{
		int w = cow[i].first - cow[i].second;
		int s = cow[i].second;
		res = max(res, num - s);
		num += w;
	} 
	 
	cout << res << endl;
	return 0;
 } 

AcWing 114. 国王游戏

这里加一道同类题。
AcWing 114. 国王游戏

题目解析:
类比上一题,我们按 ai + bi 从小到大的顺序排,最后的答案符合要求,

与上题类似,我们可以假设 ai * bi > a(i+1) * b(i+1),最后可以证明得出交换后的答案更优,即可得证。
该题 数据相乘后明显非常大,所以我们要用到高精度。

解释:vector<int>(a.rbegin(), a.rend()) > vector<int>(b.rbegin(), b.rend())

这行代码比较了两个 vector 对象 a 和 b,具体比较方式是将它们反转后再逐位比较大小。
其中,a.rbegin() 和 a.rend() 表示 a 向量的反转迭代器(即从 a 向量末元素开始的正向迭代器)和正向迭代器(指向 a 向量的开头);b.rbegin() 和 b.rend() 则表示 b 向量的反转迭代器和正向迭代器。vector(a.rbegin(), a.rend()) 和 vector(b.rbegin(), b.rend()) 分别表示两个反转后的向量,且两者具有相同的元素序列。
根据 C++ 的容器比较规则,当比较运算符应用于两个容器对象时,容器中的元素会逐个进行比较,直到遇到一个相对大小的差异或者其中一个容器被耗尽为止。在此例中,会先将两个容器反转,然后从最后一个元素开始按位比较它们的大小,直到找到第一个不同的元素,或者一个容器已经被耗尽。如果 a 向量的第 i 个元素是大于 b 向量的第 i 个元素,则返回 true,否则返回 false。
需要注意的是,这种比较方式会忽略正向的元素顺序,因此可能会对某些应用场景下的正确性产生影响。

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std; 

const int N = 1010;
typedef pair<int, int> PII;

PII person[N];

vector<int> mul(vector<int> a, int b)
{
	int t = 0;
	vector<int> c;
	for(int i = 0; i < a.size(); i++)
	{
		t += a[i] * b;
		c.push_back(t % 10);
		t /= 10;
	}
	while(t)
	{
		c.push_back(t % 10);
		t /= 10;
	}
	
	return c;
}

vector<int> div(vector<int> a, int b)
{
	int t = 0;
	vector<int> c;
	for(int i = a.size() - 1; i >= 0; i--)
	{
		t = t * 10 + a[i];
		c.push_back(t / b);
		t %= b;
	 } 
	 reverse(c.begin(), c.end());
	 while(c.size() > 1 && c.back() == 0) c.pop_back();
	 return c;
}

vector<int> max_vec(vector<int> a, vector<int> b)
{
	if(a.size() > b.size()) return a;
	if(b.size() > a.size()) return b;
	if(vector<int>(a.rbegin(), a.rend()) > vector<int>(b.rbegin(), b.rend()))
		return a;
	return b;
}

int main()
{
	int n;
	cin >> n;
	for(int i = 0; i <= n; i++)
	{
		int a, b;
		cin >> a >> b;
		person[i] = {a * b, a};
	}
	
	sort(person + 1, person + n + 1);
	
	vector<int> num(1, 1);
	vector<int> res(1, 0);
	
	for(int i = 0; i <= n; i++)
	{
		int a = person[i].second, b = person[i].first / person[i].second;
		if(i) res = max_vec(res, div(num, b));
		num = mul(num, a);
	}
	
	for(int i = res.size() - 1; i >= 0; i--)
	cout << res[i];
	cout << endl;
	return 0;
}

时间复杂度

比赛时, 时间复杂度一般为 1 s 内,
即 107 ~ 108 都是OK的,
如果超过108,常数不是特别小的话,就会超时。

在这里插入图片描述
来自acwing

比赛时,空间复杂度一般不用考虑
限制一般为 64MB
即 64MB = 1.6 * 107 字节

在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mirror_zz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值