贪心专题1

文章介绍了贪心算法的基本思想,通过三个实例——排队接水、均分纸牌和小A的糖果问题——展示了如何运用贪心策略找到局部最优解并推导出全局最优解。每个问题提供了分析、证明和C++代码实现,强调了在解决贪心问题时证明局部最优解的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

贪心专题1

简介

贪心是有一种由局部最优解推算到全局最优解的思想, 在做贪心题的时候最重要的一点就是证明为什么这个局部最优可以推出全局最优, 所以贪心题往往是较考验思维的。

P1223 排队接水
分析

这题就不用多讲了吧, 一道小学数学题

如果我没记错的话, 小学时候是以轮船运货物为背景出的题

结论

直接升序排列 总体等待的时间就是最小的, 即平均时间最小;

证明

在计算是越往前需要被乘上更大的系数, 显然是从前往后升序总体,最小。

代码
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010;

typedef pair<int, int> PII;
#define x first
#define y second

PII q[N];
int n;

int main() {
	cin >> n;
	int x;
	for (int i = 0; i < n; ++ i)
		cin >> x, q[i] = { x, i + 1 };
	sort(q, q + n);
	for (int i = 1; i <= n; ++ i)
		q[i].x += q[i - 1].x, cout << q[i - 1].y << " ";
	puts(""); 
	double sum = 0;
	for (int i = 1; i < n; ++ i)
	 sum += q[i - 1].x;
	printf("%.2f", sum / n);
	return 0;
} 

P1031 均分纸牌
分析

首先 可以发现 一定是存在一种方法能够用在 n - 1 次操作内使得数组所有的数相等

思路

我们将数组全部减去平均值 正数则代表该位置需要将牌移向别的位置
负数则代表该位置需要别的牌移向该位置
然后将该信息转移到下一个位置 代表该位置向左所以的节点需要的卡牌(可能为负)
当转移为0 的时候则代表从开始转移的l 到结束的r中,存在一种方法使得其中卡牌完全相等
且改操作数一定等于区间长度 - 1
所以我们只需要遍历找到每一个这样的区间即可

证明

1
对于

且改操作数一定等于区间长度 - 1

假设 :操作次数不需要 区间长度 - 1 那么必然存在至少一条路径没有经过
我们假设 n 不需要向n - 1 转移牌 那么说明n - 1 l 可以 通过某种操作将自己全变为平均数 这与定义(找到第一个不需要转移的点)矛盾。也就是这个区间其实是两个区间。
所以假设不成立

2
我们所有的一切都围绕着这一句话在进行

首先 可以发现 一定是存在一种方法能够用在 n - 1 次操作内使得数组所有的数相等

那么我们证明一下这个

假设 必须得 n - 1个方法以上 才能将数组全部变为一致, 那么由抽屉原理可以得出 一定存在至少一条路径是被重复走了的, 我们假设 这条路 为 nn - 1 (1) 加入两次数据的传输都是从n到n - 1 那么很明显我们一定可以合并这次传输 (2)假如我们是一次 n->n - 1 和一次 n - 1->n 我们也可以很明显的得出 这两条路劲中 至少有一条是多余的(比如 左往右传2 右往左传 3 等价于 右往左传1
所以显然 假设不成立

代码

O(n)

#include <iostream> 

using namespace std;

const int N = 110;

int q[N];
int n;

int main() {
	cin >> n;
	int sum = 0;
	for (int i = 0; i < n; ++i)
		cin >> q[i], sum += q[i];
	sum /= n;
	for (int i = 0; i < n; ++i)
		q[i] -= sum;
	int res = 0;
	for (int i = 0; i < n; ++ i)
		if (q[i] != 0) q[i + 1] += q[i], res++; // 需要转移的时候直接加上就好了
	cout << res;
	return 0;
}

P3817 小A的糖果
分析

首先肯定每一个点都不能超过x 所以我们先将每个点 去掉大于x 的部分

从左往右考虑, 如果i 与 i +1 的位置发生了冲突 我们应该修改那个位置?
显然是i +1的位置 因为对于i 与 i - 1 一定是合法的了,修改了对后面没有任何的帮助, 而修改i +1 可以使 i +1 和 i +2 更接近合法

所以我们只需要每次修改右值即可得到最小值

证明 :

分析的思路即为证明 :

如果i 与 i +1 的位置发生了冲突 我们应该修改那个位置?
显然是i +1的位置 因为对于i 与 i - 1 一定是合法的了, 修改了对后面没有任何的帮助, 而修改i +1 可以使 i +1 和 i +2 更接近合法

代码
#include <iostream>

using namespace std;

const int N = 100010;

typedef long long LL;

int q[N];
int n, x;
LL res;

int main() {
	cin >> n >> x;
	for (int i = 0; i < n; ++ i) {
		cin >> q[i];
		res += max(0, q[i] - x);
		q[i] = min(x, q[i]);
	}
	for (int i = 0; i < n - 1; ++ i) {
		LL sum = q[i] + q[i + 1];
		if (sum > x) res += sum - x, q[i + 1] -= sum - x;  
	}
	cout << res;
	return 0;
}

本次的贪心专题1就到这里结束啦!, 完结撒花!!!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值