当前位置: 首页 > article >正文

【算法篇】贪心算法

目录

贪心算法

贪心算法实际应用 

一,零钱找回问题

二,活动选择问题

三,分数背包问题

将数组和减半的最小操作次数

最大数


贪心算法

贪心算法,是一种在每一步选择中都采取当前状态下的最优策略,期望得到全局最优解的算法策略。也就是说,通过局部最优解,期望得到全局最优解。

贪心算法一般按如下步骤执行:

1,问题分解: 将原问题转化为一系列子问题。

2,贪心选择:对每个子问题求解的时候,都选择当前看起来“最优的”解法。

3,合并解:累计局部最优解形成全局解。

4,正确性证明:通过数学归纳或者替换论证验证。(因为贪心策略可能是错误的)

先简单理解一下贪心算法:

例一:找零问题

这个问题在我们日常生活中很普遍。

假设我们现在可以找的零钱面额为【20,10,5,1】,并且每个有无限张。当我们想找k的零钱时,怎样可以让钱的张数最少?

这里假设k=46,用 贪心算法的思想,每一步尽可能使用面值最大的纸币即可。

46->  选一张20  -> 26  -> 选一张20 -> 6-> 选一张5->1->选一张1

例二:最小路径和

给你一个矩阵,求解:从矩阵的左上角出发,只能向下和向右两个方向走,求到达矩阵的右下角过程中,路径上所有元素和的最小值。

贪心算法实际应用 

一,零钱找回问题

假设1元,5元,10元,20元,50元,100元的纸币分别有

c0,c1,c2,c3,c4,c5张 。现在用这些钱来找回k元,求最少使用的张数。

用贪心算法的思想,每一步找钱尽可能使用面值大的。

//单张钱的面额
int signle_money[7] = { 1,2,5,10,20,50,100 };
//对应前的个数
int number_money[7] = {2,5,1,3,4,0,4};

//存放结果
int total[7] = { 0 };

int tanxin(int money)
{
    if (money >= 0)
    {
        //每一步尽量选最大的面额
        for (int i = 6; i >=0; i--)
        {
            total[i] = min(number_money[i], money / signle_money[i]);
            money = money - total[i] * signle_money[i];
        }
        return 0;
    }
    else
    {
        return money;
    }
}
int main()
{
    int k = 0;
    cout << "输入 要找回的零钱" << endl;
    cin >> k;

    if (!tanxin(k))
    {
        cout << "贪心结果为:" << endl;
        for (int i = 0; i < 7; i++)
            cout << signle_money[i] << "元:" << total[i] << "张" << endl;
    }
    else
    {
        cout << "ops wrong number" << endl;
    }
    return 0;
}
 

  

二,活动选择问题

有n个活动,a1,a2,a3...an,这些活动需要在同一天使用同一个教室。每个活动都有一个开始时间si和结束时间fi,一旦被选择后,活动ai就占据半开时间[si,fi)。如果[si,fi)和[sj,fj)互不重叠,那么ai和aj两个活动可以被安排在同一天。该问题是安排这些活动,使尽量多的活动不冲突的举行。

贪心策略:想要使尽量多的活动不冲突,那我们在选择活动时,就尽量选择结束早的活动,为后续留出更多的时间。

//活动安排问题
#include <algorithm>
struct ACT
{
    int start;
    int end;
}activity[100];

//活动的个数
int N;

bool cmp(ACT a, ACT b)
{
    return a.end < b.end;
}

int greedy()
{
    int num = 0;

    for (int i = 0, j = i + 1; i < N; j++)
    {
        if (activity[j].start > activity[i].end)
        {
            i = j;
            num++;
        }
    }

    return num;
}

int main()
{
    cout << "活动的总数:";
    cin >> N;
    cout << "输入每个活动的开始和结束:" << endl;

    for (int i = 0; i < N; i++)
    {
        cin >> activity[i].start;
        cin >> activity[i].end;
    }
    
    //将活动按结束时间排序,升序
    sort(activity, activity + N, cmp);

    //统计结果
    int res = greedy();
    cout << "安排的活动个数:" << res << endl;
    return 0;
}
 

三,分数背包问题

情景描述:给定背包容量和一些物品,每个物品有重量和价值两个属性。允许只取一个物品的一部分加入背包,求问如何才能使背包装的物品价值最大。

与01背包不同,分数背包问题允许将物品的部分装入背包。这意味着我们可以将一个物品分割成任意大小,并根据其重量比例来计算其价值。

贪心策略:

核心思想:按性价比来排序。即每个物品的单位价值:价值/重量。

按照单位价值从高到低对物品进行排序,然后依次考虑每个物品 。

算法步骤:

   1,将所有物品按照单位价值从高到低排序。

    2,遍历排序后的物品。对于每个物品:

  • 如果物品重量小于等于背包剩余容量 ,就将物品装入背包。
  • 如果物品重量大于背包剩余容量,只装入能狗适应当前容量的部分。

//分数背包问题
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

class Item
{
public:
    int _w;
    int _v;
    Item() = default;
    Item(int w,int v)
        :_w(w)
        ,_v(v)
    {}
};

bool cmp(Item a, Item b)
{
    return a._v / a._w > b._v /a._w;
}

double greed(vector<int>& w, vector<int>& v, int cap)
{
    vector<Item> items(w.size());
    double res = 0;

    for (int i = 0; i < w.size(); i++)
    {
        items[i] = { w[i], v[i] };
    }

    sort(items.begin(), items.end(), cmp);

    for (auto& item : items)
    {
        if (item._w <= cap)
        {
            res += item._v;
            cap -= item._w;
        }
        else
        {
            res += (double)item._v / item._w * cap;
            break;
        }
    }

    return res;
}
int main()
{
    vector<int> w = { 10,20,30,40,50 };
    vector<int> v = { 50,120,150,210,240 };
    //背包容量
    int cap = 50;
    cout << "MAX_value:" << greed(w, v, cap) << endl;

    return 0;
}

                   

将数组和减半的最小操作次数

本题链接:2208. 将数组和减半的最少操作次数 - 力扣(LeetCode)

 贪心策略:每次选出数组中的最大值,对该数减半,直到使数组和减小到一半。

算法步骤:

  • 计算出数组和的一半sum
  • 每次选数组 中最大的数a进行减半,sum-=a/2,找到sum为0结束。

在每次选最大数的时候,我们可以借助优先级队列,快速找的最大值。

class Solution {
public:
    int halveArray(vector<int>& nums) {
        priority_queue<double> heap;
        double sum=0.0;
        for(int x:nums)
        {
             sum+=x;
             heap.push(x);
        }

        sum/=2.0;
        
        int count=0;
        while(sum>0)
        {
            double t=heap.top()/2.0;
            sum-=t;
            heap.pop();
            heap.push(t);
            count++;

        }
        return count;

    }
};

最大数

本题链接:179. 最大数 - 力扣(LeetCode)

贪心策略:本题可以理解为是对数组nums进行重新“排序”,而传统的排序是根据大小排的,对于本题,则是按照题目要求。

对于nums数组中的两个元素a和b, 我们无法直接确定他们的先后关系,但我们可以从结果角度来看,如果拼接结果ab要比ba好,那么a就应该放在b的前面。

class Solution {
public:
    string largestNumber(vector<int>& nums) {
        vector<string> strs;
        for(auto x:nums)
        strs.push_back(to_string(x));

        sort(strs.begin(),strs.end(),
        [](string s1,string s2)
        {
            return s1+s2>s2+s1;
        });

        string ret;
        for(auto& s:strs)
        ret+=s;
        //处理前导0
        if(ret[0]=='0') return "0";

        return ret;
    }
};


http://www.kler.cn/a/533523.html

相关文章:

  • 宾馆民宿酒店住宿管理系统+小程序项目需求分析文档
  • Hackmyvm Blackhat
  • 如何自定义软件安装路径及Scoop包管理器使用全攻略
  • Java中的object类
  • 利用Vue和javascript分别编写一个“Hello World”的定时更新
  • Vue指令v-on
  • 【Elasticsearch】索引性能优化
  • 自指学习:AGI的元认知突破
  • python读取excel工具:openpyxl | AI应用开发
  • Spring Web MVC项目的创建及使用(一)
  • doris:临时分区
  • 拉格朗日乘数法算法详解Python实现
  • Math Reference Notes: 符号函数
  • 013-51单片机红外遥控器模拟控制空调,自动制冷制热定时开关
  • java基础05——java泛型
  • Java进阶——IO 流
  • [25] cuda 应用之 nppi 实现图像色彩调整
  • Java进阶(JVM调优)——阿里云的Arthas的使用 安装和使用 死锁查找案例,重新加载案例,慢调用分析
  • 解决浏览器播放音频声音,没交互前不播放问题
  • 设计模式Python版 外观模式
  • 国产化创新 守护开放边界网络安全
  • cocos spine执行动画报错Cannot read properties of null (reading ‘data‘)
  • Android原生开发入门
  • Unity 2D实战小游戏开发跳跳鸟 - 记录显示最高分
  • leetcode——子集(java)
  • Python实现CAN FD 通信(基于PCAN开发CAN FD测试工具)