首页 > 编程语言 >贪心算法(算法详解+模板+例题)

贪心算法(算法详解+模板+例题)

时间:2024-09-16 15:20:20浏览次数:11  
标签:输出 果子 硬币 int coins amount 算法 例题 贪心

1.贪心是什么

贪心算法(Greedy Algorithm)是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好的策略。虽然这种策略并不保证一定能得到全局最优解,但在许多情况下,它能提供近似最优解,而且计算效率高。贪心算法通常适用于那些具有“最优子结构”和“贪心选择性质”的问题,比如霍夫曼编码、最小生成树等。

2.基本思路

  1. 建立数学模型:首先需要将实际问题抽象成数学模型,明确问题的目标和约束条件。
  2. 分解子问题:将原问题分解成多个子问题,这些子问题的解决方案有助于构建整个问题的解决方案。
  3. 局部最优解:对于每一个子问题,贪心算法会做出在当前状态下看似最优的选择。这种选择通常是基于某种度量标准或规则来确定的。
  4. 合成全局解:通过一系列局部最优解的组合,期望能够得到全局最优解。重要的是,一旦做出了选择,就不会再回头更改这个选择,即便后续发现有更好的选择也是如此。

 3.操作步骤

步骤:

  1. 问题建模:明确问题的边界条件、目标函数以及约束条件。
  2. 子问题划分:将问题分解为更小的子问题,这些子问题应该具有与原问题相同的结构。
  3. 贪心选择:对于每一个子问题,做出一个看起来最优的选择。这一步是贪心算法的核心。
  4. 构造解:将子问题的解组合起来,形成完整问题的一个解。
  5. 验证解:检查构造的解是否满足所有约束条件,并符合目标函数的要求。

 例子:

    假设我们有一个简单的例子,比如选择一组硬币来支付一个给定金额的情况,我们的目标是以        最少数量的硬币完成支付。

  1. 问题建模:设定硬币面值(例如1分、5分、10分、25分)和需要支付的总金额。
  2. 子问题划分:将支付金额逐步减少,每次减少一个硬币面值的金额。支付金额递减的过程,比如从37分开始,减去25分,然后减去10分,再减去1分。
  3. 贪心选择:每次选择面值最大的硬币,只要不超过剩余的支付金额。每次选择硬币的过程:先选25分,再选10分,最后选1分。
  4. 构造解:将选择的所有硬币加起来,构成最终的支付方案。最终选择的硬币组合,即25分+10分+1分。
  5. 验证解:确认所选硬币的总和等于需要支付的金额,并且硬币数量最少。验证过程,确认37分已完全支付并且只用了3枚硬币。

 4.代码模板(以硬币找零问题为例)

python模板:

def min_coins(coins, amount):
    # 对硬币面值进行降序排序
    coins.sort(reverse=True)
    
    # 初始化结果变量
    result = 0
    
    # 遍历每个硬币面值
    for coin in coins:
        # 计算当前硬币可以使用的次数
        count = amount // coin
        # 更新结果
        result += count
        # 减去已经支付的金额
        amount -= count * coin
        
        # 如果金额变为0,则返回结果
        if amount == 0:
            return result
    
    # 如果无法支付,则返回-1
    return -1

# 示例
coins = [1, 5, 10, 25]
amount = 37
print(min_coins(coins, amount))  # 输出: 3

c++模板:

#include <iostream>
#include <vector>
#include <algorithm>

int minCoins(std::vector<int>& coins, int amount) {
    // 对硬币面值进行降序排序
    std::sort(coins.begin(), coins.end(), std::greater<int>());
    
    int result = 0;
    
    // 遍历每个硬币面值
    for (int coin : coins) {
        // 计算当前硬币可以使用的次数
        int count = amount / coin;
        // 更新结果
        result += count;
        // 减去已经支付的金额
        amount -= count * coin;
        
        // 如果金额变为0,则返回结果
        if (amount == 0) {
            return result;
        }
    }
    
    // 如果无法支付,则返回-1
    return -1;
}

int main() {
    std::vector<int> coins = {1, 5, 10, 25};
    int amount = 37;
    std::cout << minCoins(coins, amount) << std::endl;  // 输出: 3
    return 0;
}

5.经典例题

p1223排队接水

题目描述

有 n 个人在一个水龙头前排队接水,假如每个人接水的时间为 Ti,请编程找出这 n 个人排队的一

种顺序,使得 n 个人的平均等待时间最小。

输入格式

第一行为一个整数 n。

第二行 n 个整数,第 i 个整数 Ti 表示第 i 个人的接水时间 Ti。

 输出格式

输出文件有两行,第一行为一种平均时间最短的排队顺序;第二行为这种排列方案下的平均等待时间(输出结果精确到小数点后两位)。

 样例 #1

 样例输入 #1
10 
56 12 1 99 1000 234 33 55 99 812

 样例输出 #1
3 2 7 8 1 4 9 6 10 5
291.90

 提示

1 < n < 1000,1 < ti <10^6,不保证 ti 不重复。

代码:

#include <bits/stdc++.h>
using namespace std;

// 定义结构体
struct ss {
    int num;  // 任务编号
    int time; // 任务时间
} a[1001];  // 结构体数组,最多存储1001个任务

int n;      // 任务总数
double ans = 0.0;  // 存储总的等待时间

// 自定义比较函数,用于排序
bool cmp(const ss &a, const ss &b) {
    return a.time < b.time;  // 按照时间从小到大排序
}

int main() {
    // 读入任务数量
    scanf("%d", &n);

    // 读入每个任务的时间戳
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i].time);
        a[i].num = i + 1;  // 设置任务编号
    }

    // 根据时间戳排序
    sort(a, a + n, cmp);

    // 计算平均等待时间和输出任务编号
    for (int i = 0; i < n - 1; i++) {
        ans += a[i].time * (n - i - 1);  // 计算每个任务的等待时间并累加
        printf("%d ", a[i].num);  // 输出当前任务的编号
    }
    printf("%d\n", a[n - 1].num);  // 输出最后一个任务的编号

    // 输出平均等待时间,保留两位小数
    printf("%.2lf\n", ans / n);

    return 0;
}
p1803凌乱的yyy / 线段覆盖

 题目背景

快 noip 了,yyy 很紧张!

 题目描述

现在各大 oj 上有 n 个比赛,每个比赛的开始、结束的时间点是知道的。

yyy 认为,参加越多的比赛,noip 就能考的越好(假的)。

所以,他想知道他最多能参加几个比赛。

由于 yyy 是蒟蒻,如果要参加一个比赛必须善始善终,而且不能同时参加 2 个及以上的比赛。

 输入格式

第一行是一个整数 n,接下来 n 行每行是 2 个整数 ai,bi(ai<bi),表示比赛开始、结束的时间。

 输出格式

一个整数最多参加的比赛数目。

 样例 #1

 样例输入 #1
3
0 2
2 4
1 3

 样例输出 #1
2

 提示

- 对于 20% 的数据,n < 10;
- 对于 50% 的数据,n < 10^3;
- 对于 70% 的数据,n < 10^5;
- 对于 100% 的数据,1< n < 10^6,0 < ai < bi <10^6。

代码:

#include <bits/stdc++.h>
using namespace std;

// 定义结构体
struct node {
    int st;  // 开始时间
    int ed;  // 结束时间

    // 自定义比较运算符
    bool operator<(const node &tmp) const {
        if (ed == tmp.ed) {
            return st < tmp.st;  // 如果结束时间相同,则按照开始时间排序
        } else {
            return ed < tmp.ed;  // 否则按照结束时间排序
        }
    }
} a[1000010];  // 结构体数组,最多存储1000010个时间段

int main() {
    int n, x, y, s, cnt;
    cnt = 0;
    s = 0;

    // 读入时间段数量
    cin >> n;

    // 读入每个时间段的开始时间和结束时间
    for (int i = 0; i < n; i++) {
        cin >> a[i].st >> a[i].ed;
    }

    // 按照结束时间排序
    sort(a, a + n);

    // 计算最少覆盖点
    for (int i = 0; i < n; i++) {
        if (a[i].st >= s) {
            s = a[i].ed;  // 更新覆盖点的位置
            cnt++;  // 覆盖点数量加1
        }
    }

    // 输出最少覆盖点的数量
    cout << cnt << endl;

    return 0;
}
p1090[NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G

题目描述

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n-1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有 3 种果子,数目依次为 1 , 2 , 9 。可以先将 1 、 2 堆合并,新堆数目为 3 ,耗费体力为 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12 ,耗费体力为 12 。所以多多总共耗费体力 =3+12=15 。可以证明 15 为最小的体力耗费值。

 输入格式

共两行。  
第一行是一个整数 n(1<= n <= 10000) ,表示果子的种类数。  

第二行包含 n 个整数,用空格分隔,第 i 个整数 ai(1<=ai<= 20000) 是第 i 种果子的数目。

 输出格式

一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 2^31 。

 样例 #1

 样例输入 #1

1 2 9

 样例输出 #1
15
 提示

对于 30% 的数据,保证有 n < 1000:

对于 50% 的数据,保证有 n < 5000;

对于全部的数据,保证有 n < 10000。

代码:

#include <bits/stdc++.h>
using namespace std;

long long n, a[100010];
int main() {
    // 读入数组长度
    cin >> n;

    // 读入数组元素
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    // 将数组从小到大排序
    sort(a + 1, a + n + 1);

    long long ans = 0;

    // 主循环,进行合并操作
    while (true) {
        int i = 1;
        
        // 找到第一个非零元素
        while (a[i] == 0) {
            i++;
        }
        
        // 如果已经只剩下一个非零元素,退出循环
        if (i == n) {
            break;
        } else {
            // 合并当前元素和下一个元素
            a[i] += a[i + 1];
            
            // 累加合并代价
            ans += a[i];
            
            // 移动数组元素,删除已合并的元素
            for (int l = i + 1; l < n; l++) {
                a[l] = a[l + 1];
            }
            
            // 数组长度减一
            n--;
            
            // 再次排序,确保数组有序
            for (int l = i; l < n; l++) {
                if (a[l] > a[l + 1]) {
                    swap(a[l], a[l + 1]);
                }
            }
        }
    }

    // 输出最终答案
    cout << ans << endl;

    return 0;
}

标签:输出,果子,硬币,int,coins,amount,算法,例题,贪心
From: https://blog.csdn.net/Alex_Fufu/article/details/142302573

相关文章

  • 小林coding学习笔记(内存页面置换算法)
    缺页中断示意图1在CPU里执行一条查找某个页面A的指令,首先是虚拟内存,会到虚拟内存和物理内存的映射关系的页表中查询。2页表中不存在需要查找的页面A的有效信息。3则触发缺页中断信号给操作系统,操作系统收到缺页中断信号后,就会去磁盘里面查找该页面。4操作系统在磁盘中查......
  • 算法入门-贪心1
    第八部分:贪心409.最长回文串(简单)给定一个包含大写字母和小写字母的字符串s,返回通过这些字母构造成的最长的回文串的长度。在构造过程中,请注意区分大小写。比如"Aa"不能当做一个回文字符串。示例1:输入:s="abccccdd"输出:7解释:我们可以构造的最长的回文串是"......
  • 小林coding学习笔记(进程调度算法)
    //进程调度算法进程调度算法是CPU通过进程调度算法决定某个时刻去调用哪个进程到CPU上运行的算法1、先来先服务调度算法每次从就绪队列的队头调度到CPU上运行,直到进程退出或被阻塞,才会继续从队列中调度进程运行。特点:对短作业不利,对长作业有利,无法平衡短作业与长作业。2、最......
  • 【检索稳定,JPCS出版】第二届应用统计、建模与先进算法国际学术会议(ASMA2024,9月27日-29
    大会简介由哈尔滨理工大学主办的第二届应用统计、建模与先进算法国际学术会议(ASMA2024)将于2024年9月27日-29日于中国哈尔滨召开。会议将围绕应用统计、建模及先进算法等在数学领域中的最新研究成果,为来自国内外高等院校、科学研究所、企事业单位的专家、教授、学者、工......
  • 安全帽图像识别算法
    安全帽图像识别算法依据AI深度学习+边缘计算,通过机器视觉ai分析检测算法可以有效识别工人是不是合规和佩戴安全帽,安全帽图像识别算法提高视频监控不同场景下的主动分析与识别报警能力。安全帽图像识别算法系统搭载了全新的人工智能图像识别技术实时分析现场监控画面图像,与人力监管......
  • 代码随想录算法训练营,9月16日 | 235. 二叉搜索树的最近公共祖先,701.二叉搜索树中的插
    235.二叉搜索树的最近公共祖先题目链接:235.二叉搜索树的最近公共祖先文档讲解︰代码随想录(programmercarl.com)视频讲解︰二叉搜索树的最近公共祖先日期:2024-09-16想法:相比于普通二叉树,二叉搜索树从上往下遍历,在qp中间的值的一定是公共祖先,而第一个则是最近,因为此时你在这个祖......
  • 代码随想录算法训练营Day5 | 哈希表理论基础、242.有效的字母异位词、349.两个数组的
    哈希表理论基础哈希表哈希表是根据关键码的值而直接进行访问的数据结构。数组就是一张哈希表,哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素,如下图所示:哈希表一般用来快速判断一个元素是否出现集合里。哈希函数哈希函数通过特定编码方式,可以将其......
  • 禁忌搜索算法(TS算法)求解实例---旅行商问题 (TSP)
    目录一、采用TS求解TSP二、旅行商问题2.1实际例子:求解6个城市的TSP2.2==**求解该问题的代码**==2.3代码运行过程截屏2.4代码运行结果截屏(后续和其他算法进行对比)三、==如何修改代码?==3.1减少城市坐标,如下:3.2增加城市坐标,如下:四、禁忌搜索算法(TabuSearc......
  • 蚁群算法(ACO算法)求解实例---旅行商问题 (TSP)
    目录一、采用ACO求解TSP二、旅行商问题2.1实际例子:求解6个城市的TSP2.2==**求解该问题的代码**==2.3代码运行过程截屏2.4代码运行结果截屏(后续和其他算法进行对比)三、==如何修改代码?==3.1减少城市坐标,如下:3.2增加城市坐标,如下:四、蚁群算法(AntColonyOp......
  • 一文让你的计算机图形学从入门到入坟,从画线算法=>光线追踪=>GPU的并行加速与手搓仿真平
    文章目录前言一.计算机图形学是什么?有什么?为什么学?当前发展?二.基础概念2.120道基础知识Q&A2.2计算机图形学设备及组成2.2.1设备分类2.2.2输入设备2.2.3输出设备2.3帧缓存原理详细解释2.3.1帧缓存的基本概念2.3.2帧缓存的结构2.3.3总结2.3OpenGL的基础知识......