贪心算法:基础入门篇

这篇具有很好参考价值的文章主要介绍了贪心算法:基础入门篇。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

贪心算法:基础入门篇

一、认识贪心算法

在求最优解的问题中,以某种贪心标准,从状态的最初始找到每一步最优解,通过多次的贪心求解,最终得到整个问题的最优解,此种解题的方法为贪心算法。可见贪心算法并不是一种固定的算法,而是根据问题的条件而产生的一种解决问题的思维模式。

由定义可知,贪心算法是由局部的最优解,得到总体的最优解,因此在使用贪心算法之前,要先判断问题是否适合使用贪心算法。

二、常见贪心问题

2.1 纸牌问题

纸牌问题是最简单,也最好理解的贪心问题,题目如下:

题目描述:

N N N 堆纸牌,编号分别为 1 , 2 , … , N 1,2,\ldots,N 1,2,,N。每堆上有若干张,但纸牌总数必为 N N N 的倍数。可以在任一堆上取若干张纸牌,然后移动。

移牌规则为:在编号为 1 1 1 堆上取的纸牌,只能移到编号为 2 2 2 的堆上;在编号为 N N N 的堆上取的纸牌,只能移到编号为 N − 1 N-1 N1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。

例如 N = 4 N=4 N=4 时, 4 4 4 堆纸牌数分别为 9 , 8 , 17 , 6 9,8,17,6 9,8,17,6

移动 3 3 3 次可达到目的:

  • 从第三堆取 4 4 4 张牌放到第四堆,此时每堆纸牌数分别为 9 , 8 , 13 , 10 9,8,13,10 9,8,13,10
  • 从第三堆取 3 3 3 张牌放到第二堆,此时每堆纸牌数分别为 9 , 11 , 10 , 10 9,11,10,10 9,11,10,10
  • 从第二堆取 1 1 1 张牌放到第一堆,此时每堆纸牌数分别为 10 , 10 , 10 , 10 10,10,10,10 10,10,10,10

输入格式:

第一行共一个整数 N N N,表示纸牌堆数。
第二行共 N N N 个整数 A 1 , A 2 , … , A N A_1,A_2,\ldots,A_N A1,A2,,AN,表示每堆纸牌初始时的纸牌数。

输出格式:

共一行,即所有堆均达到相等时的最少移动次数。

样例:

样例输入

4
9 8 17 6

样例输出

3

提示

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 100 1 \le N \le 100 1N100 1 ≤ A i ≤ 10000 1 \le A_i \le 10000 1Ai10000

【题目来源】

NOIP 2002 提高组第一题


题目解析:

根据移牌规则,可以得出以下通式:
{ a i > a v e , a i + 1 = ( a i − a v e ) + a i + 1 a i = a v e a i < a v e , a i + 1 = a i + 1 − ( a v e − a i ) \left\{\begin{matrix}a_i> ave,a_{i+1}=(a_i-ave)+a_{i+1}\\a_i=ave \\a_i< ave,a_{i+1}=a_{i+1}-(ave-a_i)\end{matrix}\right. ai>ave,ai+1=(aiave)+ai+1ai=aveai<ave,ai+1=ai+1(aveai)
在左至右开始排列的情况下,从移牌规则中可得知,大于平均数的牌只能向左移动,反之小于平均数的牌只能向右移动。由此可以画出以下关系图:

根据上述思路即可写出相应代码:
#include <iostream>
using namespace std;
int main(){
    int a,sum=0,ave=0;
    int b[100];
    cin>>a;
    for(int i=0;i<a;i++){
        cin>>b[i];
        ave+=b[i];
    }
    ave=ave/a;
    for(int i=0;i<a;i++){
        if(b[i]>ave){
            b[i+1]+=b[i]-ave;
            sum++;
        }
        if(ave>b[i]){
            b[i+1]=b[i+1]-(ave-b[i]);
            sum++;
        }
    }
    cout<<sum;
    return 0;
}


2.2 背包问题(基础版)

背包问题是贪心算法中比较经典的贪心问题,同时背包问题也是一个经典的动态规划问题,其基本形式是:给定一个背包,容量为C,和一组物品,每个物品有自己的重量和价值,现在从这些物品中选择一些装入背包,要求背包中物品的总重量不超过C,且总价值最大。

接下来以一个题目来进行讲解:
题目描述

阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有 N ( N ≤ 100 ) N(N \le 100) N(N100) 堆金币,第 i i i 堆金币的总重量和总价值分别是 m i , v i ( 1 ≤ m i , v i ≤ 100 ) m_i,v_i(1\le m_i,v_i \le 100) mi,vi(1mi,vi100)。阿里巴巴有一个承重量为 T ( T ≤ 1000 ) T(T \le 1000) T(T1000) 的背包,但并不一定有办法将全部的金币都装进去。他想装走尽可能多价值的金币。所有金币都可以随意分割,分割完的金币重量价值比(也就是单位价格)不变。请问阿里巴巴最多可以拿走多少价值的金币?

输入格式

第一行两个整数 N , T N,T N,T

接下来 N N N 行,每行两个整数 m i , v i m_i,v_i mi,vi

输出格式

一个实数表示答案,输出两位小数

样例

样例输入

4 50
10 60
20 100
30 120
15 45

样例输出 #1

240.00

【题目来源】

洛谷 P2240 【深基12.例1】部分背包问题


题目解析:

根据题目可知,金币堆可以拆分,并将价值最大的情况带走。对于此题,我们需求出每堆金币的单价,找出单价最高的,然后依次搜索,到背包装满。

根据此流程图可写出代码:

#include <iostream>
#include <iomanip>
using namespace std;
int main() {
    int n, t;
    float money=0;
    float a[100][3];
    cin >> n >> t;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < 2; j++) {
            cin >> a[i][j];
            a[i][2] = a[i][1] / a[i][0];
        }
to:
    float max = 0;
    int max_num;
    for (int i = 0; i < n; i++) {
        if (max < a[i][2]) {
            max = a[i][2];
            max_num = i;
        }
    }
    if (t <= a[max_num][0]) {
        money += t * max;
    }
    else {
        money += a[max_num][0] * max;
        t = t - a[max_num][0];
        a[max_num][2] = 0;
        goto to;
    }
    cout << fixed << setprecision(2) << money;
    return 0;
}


2.3 简单数学证明问题

在数学问题中,存在多分支的情况下,不知道最优解时,可用贪心算法找出最优解。
题目描述

n n n 个人在一个水龙头前排队接水,假如每个人接水的时间为 T i T_i Ti,请编程找出这 n n n 个人排队的一种顺序,使得 n n n 个人的平均等待时间最小。

输入格式

第一行为一个整数 n n n

第二行 n n n 个整数,第 i i i 个整数 T i T_i Ti 表示第 i i i 个人的等待时间 T i T_i Ti

输出格式

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

样例

样例输入

10 
56 12 1 99 1000 234 33 55 99 812

样例输出

3 2 7 8 1 4 9 6 10 5
291.90

提示

1 ≤ n ≤ 1000 1\le n \leq 1000 1n1000 1 ≤ t i ≤ 1 0 6 1\le t_i \leq 10^6 1ti106,不保证 t i t_i ti 不重复。

【题目来源】

洛谷 P1223 排队接水


题目解析:
根据题目可知道,要让平均等待时长最少,就需要将总等待时长控制在最小,因此需要将用时最短的人,最先安排接水,就可以得到最短总时长。数学表达式如下:
f m i n _ t i m e ( x ) = ∑ i = 1 n ( n − i ) T i f_{min\_time}(x)= \sum_{i=1}^{n} (n-i)T_i fmin_time(x)=i=1n(ni)Ti
此题通过简单的排序算法,即可解决。代码如下:

#include <iostream>
#include <iomanip>
using namespace std;
int main() {
    double t = 0;
    int n;
    int a[1001][2];
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i][0];
        a[i][1] = i;
    }
    for (int i = 1; i <= n ; i++) {
        for (int j = 1; j <= n - i; j++) {
            if (a[j][0] > a[j + 1][0]) {
                int temp = a[j + 1][0];
                a[j+1][0] = a[j][0];
                a[j][0] = temp;
                int temp1 = a[j + 1][1];
                a[j+1][1] = a[j][1];
                a[j][1] = temp1;
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        cout << a[i][1] << ' ';
        t += (n - i) * a[i][0];
    }
    t = t / n;
    cout << fixed << setprecision(2) <<endl <<t;
}


三、总结

贪心算法所作的选择来源于以往的选择,并非将来的选择。贪心算法相对于其他算法有一定的速度优势,在一题可以多解的情况下,可以优先选择贪心算法。文章来源地址https://www.toymoban.com/news/detail-643214.html

到了这里,关于贪心算法:基础入门篇的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 贪心算法基础及leetcode例题

    参考 本质: 找到每个阶段的局部最优,然后去推导得到全局最优 两个极端: 常识很难: 很多同学通过了贪心的题目,但都不知道自己用了贪心算法,因为贪心有时候就是常识性的推导,所以会认为本应该就这么做! 套路: 贪心没有套路,说白了就是常识性推导加上举反例

    2023年04月20日
    浏览(72)
  • 【地铁上的面试题】--基础部分--数据结构与算法--动态规划和贪心算法

    一、动态规划的基本概念和思想 1.1 动态规划的定义和特点 动态规划是一种解决多阶段决策问题的算法思想,它通过将问题划分为若干个子问题,并保存子问题的解来求解原问题的方法。动态规划的特点包括以下几个方面: 最优子结构性质:动态规划问题具有最优子结构,即

    2024年02月12日
    浏览(56)
  • 算法分析与设计考前冲刺 (算法基础、数据结构与STL、递归和分治、 动态规划、贪心算法、 回溯算法)

    算法分析与设计考前冲刺 算法基础 算法是一系列解决问题的清晰指令,代表着用系统的方法描述解决问题的策略机制。 程序是算法用某种程序设计语言的具体的 具体实现 算法特征: 有穷性(有限步) 确定性 输入 输出 可行性(有限时间) 算法的复杂性: 时间复杂性 和空间复

    2024年02月02日
    浏览(42)
  • 五种基础算法小结与典型题目分享(动态规划、分治、贪心、回溯、分支限界)

    动态规划是用于解决多阶段决策问题的算法策略。它通过用变量集合描述当前情境来定义“状态”,进而用这些状态表达每个阶段的决策。 每个阶段的状态是基于前面的状态经过某种决策得到的。通过建立状态间的递推关系,并将其形式化为数学递推式,得到“状态转移方程

    2024年01月19日
    浏览(63)
  • 算法训练day31贪心算法理论基础Leetcode455分发饼干376摆动序列53最大子序和

    文章链接 代码随想录 (programmercarl.com) 说实话贪心算法并没有固定的套路 。 最好用的策略就是举反例,如果想不到反例,那么就试一试贪心吧 。 面试中基本不会让面试者现场证明贪心的合理性,代码写出来跑过测试用例即可,或者自己能自圆其说理由就行了 。 刷题或者面

    2024年02月20日
    浏览(45)
  • Day31 贪心算法 part01 理论基础 455.分发饼干 376.摆动序列 53.最大子序和

    什么是贪心 贪心的本质是选择每一阶段的局部最优,从而达到全局最优 。 这么说有点抽象,来举一个例子: 例如,有一堆钞票,你可以拿走十张,如果想达到最大的金额,你要怎么拿? 指定每次拿最大的,最终结果就是拿走最大数额的钱。 每次拿最大的就是局部最优,最

    2024年01月19日
    浏览(44)
  • 贪心算法问题实验:贪心算法解决TSP问题

    TSP问题是指旅行商问题,即给定一组城市和每对城市之间的距离,求解访问每一座城市一次并回到起始城市的最短回路。它是组合优化中的一个NP困难问题,在运筹学和理论计算机科学中有着广泛的应用。 贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最

    2024年02月03日
    浏览(42)
  • 排序算法:基础入门篇

    1.1 常规选排思路 选择排序算法是排序算法中,最简单直观的排序法,其思路也是简单明了,它的时间复杂度为 O ( n 2 ) O(n^2) O ( n 2 ) ,空间复杂度为 O ( 1 ) O(1) O ( 1 ) 。选择排序具有不稳定的特点,因此我们在排序量大的时候,尽量不选择选择排序,会导致运行效率大大降低,

    2024年02月12日
    浏览(26)
  • 【算法入门】字符串基础

    字符串通常以\\0作为结束标志,\\0的ASCll码值为0,计算字符串长度时会忽略斜杠零。 1.字符串相关库函数 在讲解题目之前我们先介绍几个关于字符串操作常用的几个库函数 💫(1) strcpy函数 💫 strcp y也叫 拷贝函数 ,头文件为 string.h ,顾名思义它可以将一个字符串数组的内容

    2024年02月02日
    浏览(52)
  • ACwing算法基础入门代码合集

    快速排序 786.第k个数 归并排序 787.归并排序 788.逆序对的数量 二分 789.数的范围 790.数的三次方根 高精度 791.高精度加法(山西大学2023机试第三题) 792.高精度减法 793.高精度乘法 794.高精度除法 前缀和与差分 795.前缀和 796.子矩阵的和 797.差分 双指针算法 799.最长连续不重复子

    2024年01月25日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包