简单贪心算法 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 -> 与贪心策略矛盾 -> 所以贪心策略正确

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


相关推荐
leoufung14 小时前
LeetCode 149: Max Points on a Line - 解题思路详解
算法·leetcode·职场和发展
样例过了就是过了14 小时前
LeetCode热题100 最长公共子序列
c++·算法·leetcode·动态规划
HXDGCL14 小时前
矩形环形导轨:自动化循环线的核心运动单元解析
运维·算法·自动化
谭欣辰14 小时前
C++ 排列组合完整指南
开发语言·c++·算法
代码中介商15 小时前
银行管理系统的业务血肉 —— 流程、状态机、输入校验与持久化(下篇)
c语言·算法
橙子也要努力变强15 小时前
信号捕捉底层机制-机理篇2
linux·服务器·c++
foundbug99915 小时前
自适应滤除直达波干扰的MATLAB实现
开发语言·算法·matlab
盐焗鹌鹑蛋15 小时前
【C++】stack和queue类
c++
郝学胜-神的一滴16 小时前
罗德里格斯旋转公式(Rodrigues‘ Rotation Formula)完整推导
c++·unity·godot·图形渲染·three.js·unreal
lzh2004091916 小时前
深入理解进程:从PCB内核结构到写时拷贝的底层实战
linux·c++