简单贪心算法 cpp


8. 贪心

题目 两极分化

  1. 贪心算法

    1. 即贪心策略 : 用局部最优找出全局最优

      1. 把解决问题的过程分成若干步

      2. 每一步都用看起来最优的解法

      3. 以此希望 局部最优 -> 全局最优

  2. 贪心算法的特点

    1. 贪心 -> 贪婪 + 鼠目寸光 -> 有可能出现错误 -> 需要证明策略正确 (反证/数学归纳/交换论证/...)

    2. 问题不同 , 贪心策略不同 -> 没有固定模板

  3. 学贪心的方法

    1. 认知 : 遇到新的贪心题 若不会 很正常

    2. 前期学习 , 重在学习策略 , 把策略当经验吸收

    3. 练习时 , 尽可能证明策略的正确性


8.1 简单贪心

8.1.1 货仓地址

!洛谷

P10452 货仓选址

P10452 货仓选址 - 洛谷

题目描述

在一条数轴上有 N N N 家商店,它们的坐标分别为 A 1 ∼ A N A_1 \sim A_N A1∼AN。

现在需要在数轴上建立一家货仓,每天清晨,从货仓到每家商店都要运送一车商品。

为了提高效率,求把货仓建在何处,可以使得货仓到每家商店的距离之和最小。

输入格式

第一行输入整数 N N N。

第二行 N N N 个整数 A 1 ∼ A N A_1 \sim A_N A1∼AN。

输出格式

输出一个整数,表示距离之和的最小值。

输入输出样例 #1

输入 #1

复制代码
4
6 2 9 1

输出 #1

复制代码
12

说明/提示

数据保证, 1 ≤ N ≤ 10 5 1 \le N \le 10^5 1≤N≤105, 0 ≤ A i ≤ 40000 0 \le A_i \le 40000 0≤Ai≤40000。

思路:

结论 : 建立在中位数的地方 (n为奇 -> 中位数 , n 为偶 -> 两个中位数都可以), 距离之和最小

注意要排序

代码:
cpp 复制代码
#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;
typedef long long LL;

LL n;
LL a[N], sum;

int main()
{
	cin >> n;
	
	for(int i = 1; i <= n; i++ ) cin >>  a[i];

	sort(a + 1, a + 1 + n);

	for(int i = 1; i <= n; i++) sum += abs(a[n/2] - a[i]);	
		
	cout << sum;	

	return 0;
 } 
证明:

绝对值不等式 : ∣ a − x ∣ + ∣ b − x ∣ ≥ ∣ a − b ∣ |a-x|+|b-x| ≥ |a-b| ∣a−x∣+∣b−x∣≥∣a−b∣

性质 : 当 x 在 ( a , b ) (a,b) (a,b) 之间时, 取等号 , 并取最小值

结论 :

形如
sum = ∑ i = 1 n ∣ a [ i ] − x ∣ = ∣ a [ 1 ] − x ∣ + ∣ a [ 2 ] − x ∣ + ... + ∣ a [ n ] − x ∣ \text{sum} = \sum_{i=1}^{n} |a[i] - x| = |a[1] - x| + |a[2] - x| + \ldots + |a[n] - x| sum=i=1∑n∣a[i]−x∣=∣a[1]−x∣+∣a[2]−x∣+...+∣a[n]−x∣

的式子

  • 当 x 取得 n 个数的中位数时 , 和最小
  • 和最小为 : ( a [ n ] − a [ 1 ] ) + ( a [ n − 1 ] − a [ 2 ] ) + ... + ( a [ n + 1 − n / 2 ] − a [ n / 2 ] ) (a[n]−a[1])+(a[n−1]−a[2])+...+(a[n+1−n/2]−a[n/2]) (a[n]−a[1])+(a[n−1]−a[2])+...+(a[n+1−n/2]−a[n/2])

先大胆猜想策略 , 然后验证


8.1.2 最大子段和

!洛谷

P1115 最大子段和

P1115 最大子段和 - 洛谷

题目描述

给出一个长度为 n n n 的序列 a a a,选出其中连续且非空的一段使得这段和最大。

输入格式

第一行是一个整数,表示序列的长度 n n n。

第二行有 n n n 个整数,第 i i i 个整数表示序列的第 i i i 个数字 a i a_i ai。

输出格式

输出一行一个整数表示答案。

输入输出样例 #1

输入 #1

复制代码
7
2 -4 3 -1 2 -4 3

输出 #1

复制代码
4

说明/提示

样例 1 解释

选取 [ 3 , 5 ] [3, 5] [3,5] 子段 { 3 , − 1 , 2 } \{3, -1, 2\} {3,−1,2},其和为 4 4 4。

数据规模与约定
  • 对于 40 % 40\% 40% 的数据,保证 n ≤ 2 × 10 3 n \leq 2 \times 10^3 n≤2×103。
  • 对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 2 × 10 5 1 \leq n \leq 2 \times 10^5 1≤n≤2×105, − 10 4 ≤ a i ≤ 10 4 -10^4 \leq a_i \leq 10^4 −104≤ai≤104。

2026/01/21:增加一组 hack 数据

思路:

贪心策略 :

  • 从第一个元素起 累加

  • sum >= 0 : 继续累加

  • sum < 0 : 舍去这一段 , 从下一个位置开始重新累加

代码:
cpp 复制代码
#include<bits/stdc++.h>

using namespace std;

typedef long long LL;

int main()
{
	int n;
	cin >> n;
	
	LL sum = 0 , a , ret = -1e6;
	
	while(n--)
	{
		cin >> a;
		sum += a;
		ret = max(ret , sum);
		if(sum < 0) sum = 0;
	}
	
	cout << ret << endl;
	
	return 0;
}
证明:

------±-----±----±-----±----±---

----- a---- c --- k1 -- b -- k2

  • 从 ( a , b ) (a,b) (a,b) 之间任何一个点开始 , 都不可能是最优解

  • 反证法 :

    1. 从c点开始 , 向后累加时 , 在 ( a , b ) (a,b) (a,b) 之间拿到更优解

      s u m [ c , k ] > s u m [ a , k ] sum[c,k] > sum[a,k] sum[c,k]>sum[a,k] -> s u m [ a , c ] < 0 sum[a,c]<0 sum[a,c]<0 -> 与贪心策略矛盾 -> 所以贪心策略正确

    2. 从c点开始 , 向后累加时 , 越过b点 , 拿到最优解

      s u m [ c , b ] > s u m [ a , b ] sum[c,b] > sum[a,b] sum[c,b]>sum[a,b] -> s u m [ a , c ] < 0 sum[a,c] < 0 sum[a,c]<0 -> 与贪心策略矛盾 -> 所以贪心策略正确

看着思路离谱 , 在贪心算法中 , 很正常 , 重要的是证明


相关推荐
2501_945423542 小时前
模板编程中的SFINAE技巧
开发语言·c++·算法
AMoon丶2 小时前
Golang--垃圾回收
java·linux·开发语言·jvm·后端·算法·golang
承渊政道2 小时前
【优选算法】(实战感悟二分查找算法的思想原理)
c++·笔记·学习·算法·leetcode·visual studio code
☆5662 小时前
C++中的策略模式应用
开发语言·c++·算法
2401_884563242 小时前
C++中的原型模式变体
开发语言·c++·算法
重生之我是Java开发战士2 小时前
【递归、搜索与回溯】记忆化搜索:斐波那契数列,不同路径,最长递增子序列,猜数字游戏II,矩阵中最长递增路径
算法·leetcode·深度优先
干啥啥不行,秃头第一名2 小时前
C++与机器学习框架
开发语言·c++·算法
爱吃涮毛肚的肥肥(暂时吃不了版)2 小时前
Leetcode——181.超过经理收入的员工
算法·leetcode·职场和发展
永远睡不够的入2 小时前
C++庖丁解牛:深入理解多态:从虚函数表到底层实现
开发语言·c++