20240312-2-贪心算法

贪心算法

在这里插入图片描述

是每次只考虑当前最优,目标证明每次是考虑当前最优能够达到局部最优,这就是贪心的思想,一般情况下贪心和排序一起出现,都是先根据条件进行排序,之后基于贪心策略得到最优结果。
面试的时候面试官一般不会出贪心算法,如果可能贪心一般都可以使用动态规划解决,面试官很喜欢出动态规划的题目。

1. 最大连续子序列

题目: 给定一个整数数组,找到一个具有最大和的子数组,返回其最大和。
扩展1: 给定一个整数数组,找出两个 不重叠 子数组使得它们的和最大。
扩展2: 给定一个整数数组,找出两个不重叠的子数组A和B,使两个子数组和的差的绝对值|SUM(A) - SUM(B)|最大。
分析: 使用这个s表示当前可能满足的最大和,如果s>0,我们认为s对接下来的加操作有帮助,基于s+=nums[i],if s < 0, 认为s只会对后面造成负影响,两s=nums[i]。
扩展问题: 可以将 数组从每个位置k分开,分别结算[1,i]和[i+1, n)的最值,记录的过程中可以使用数组保存下来的已经计算好的值。

int maxSubArray(vector<int> &nums) {
    int s = 0, ans = -1000000;
    for(int i = 0; i < nums.size(); i ++) {
        if(s > 0) s += nums[i];
        else s = nums[i];
        ans = max(s, ans);
    }
    return ans;
}

2. 删除数字

题目: 给定一个以字符串表示的非负整数,从该数字中移除掉k个数位,让剩余数位组成的数字尽可能小,求可能的最小结果。
分析: 从左到右遍历字符串,找到第一个不满足递增的数字删除,一定会保证当前操作之后剩下的数字最小。

string removeKdigits(string &num, int k) {
    int i;
    while(k --) {
        for(i = 0; i < num.size() - 1 && num[i] <= num[i+1]; i ++);
        num.erase(num.begin() + i);
    }
    // remove 0
    auto it = num.begin();
    while(it != num.end() && *it == '0') {
        num.erase(it);
        it = num.begin();
    }
    if(num.size() == 0) num = "0";
    return num;
}

3. 无重叠区间

题目: 给定一些区间,找到需要移除的最小区间数,以使其余的区间不重叠。
分析: 贪心一般伴随着排序一起出现,我们根据区间的结束使用升序排序,之后进行遍历,如果发现不满足条件,则移除这个不满足的区间。

classs Interval {
    int start, end;
    Interval(int start, int end) {
        this->start = start;
        this->end = end;
    }
}
bool cmp(Interval a, Interval b) {
    if(a.end < b.end) return 1;
    else return 0;
}     
int eraseOverlapIntervals(vector<Interval> &intervals) {
    sort(intervals.begin(), intervals.end(), cmp);    
    int cnt = 0;
    Interval tmp = intervals[0];
    for(int i = 1; i < intervals.size(); i ++) {
        if(tmp.end <= intervals[i].start) tmp = intervals[i];
        else {
            cnt ++;
        }
    }
    return cnt;
}

4. 合并数字

题目: 给出n个数,现在要将这n个数合并成一个数,每次只能选择两个数a,b合并,每次合并需要消耗a+b的能量,输出将这n个数合并成一个数后消耗的最小能量。
分析: 参考哈夫曼树的构造,每一次合并两个最小的数,直到剩下一个数字,因为每次要选择两个最小的,需要用到最小堆来实现,可以使用C++SLT中的优先队列.
根据这个题目,请大家自行补上哈夫曼树

int mergeNumber(vector<int> &numbers) {
    priority_queue<int, vector<int>, greater<int>> pq;
    for(int i = 0; i < numbers.size(); i ++) {
        pq.push(numbers[i]);
    }
    int cost = 0;
    while(pq.size() > 1) {
        int a = pq.top();
        pq.pop();
        int b = pq.top();
        pq.pop();
        cost += (a + b);
        pq.push(a + b);
    }
    return cost;
}

5. 最小支撑树

题目: 使用kruskal算法,构造最小支撑树。
分析: 详见百度百科或者wikipedia.
代码: kruskal code

struct Edge { 
    int src, dest, weight; 
}; 
struct Graph { 
    int V, E;   
    struct Edge* edge; 
}; 
struct Graph* createGraph(int V, int E) { 
    struct Graph* graph = new Graph; 
    graph->V = V; 
    graph->E = E;   
    graph->edge = new Edge[E]; 
    return graph; 
} 
struct subset { 
    int parent; 
    int rank; 
}; 
int find(struct subset subsets[], int i) { 
    if (subsets[i].parent != i) 
        subsets[i].parent = find(subsets, subsets[i].parent); 
    return subsets[i].parent; 
} 
void Union(struct subset subsets[], int x, int y) { 
    int xroot = find(subsets, x); 
    int yroot = find(subsets, y);   
    if (subsets[xroot].rank < subsets[yroot].rank) 
        subsets[xroot].parent = yroot; 
    else if (subsets[xroot].rank > subsets[yroot].rank) 
        subsets[yroot].parent = xroot;   
    else { 
        subsets[yroot].parent = xroot; 
        subsets[xroot].rank++; 
    } 
} 
int myComp(const void* a, const void* b) { 
    struct Edge* a1 = (struct Edge*)a; 
    struct Edge* b1 = (struct Edge*)b; 
    return a1->weight > b1->weight; 
} 
void KruskalMST(struct Graph* graph) { 
    int V = graph->V; 
    struct Edge result[V];  
    int e = 0; 
    int i = 0; 
    qsort(graph->edge, graph->E, sizeof(graph->edge[0]), myComp);   
    struct subset *subsets = 
        (struct subset*) malloc( V * sizeof(struct subset) ); 
    for (int v = 0; v < V; ++v) { 
        subsets[v].parent = v; 
        subsets[v].rank = 0; 
    }   
    while (e < V - 1) { 
        struct Edge next_edge = graph->edge[i++];   
        int x = find(subsets, next_edge.src); 
        int y = find(subsets, next_edge.dest);   
        if (x != y) { 
            result[e++] = next_edge; 
            Union(subsets, x, y); 
        } 
    }   
    return; 
} 

6. 补齐数组

题目: 给出一个正整数数组nums和一个整数n,向数组添加patch元素,使得范围[1, n]包含的任何数字都可以由数组中某些元素的总和形成。返回所需的最少补齐数。
分析:

  1. 升序排序,
  2. 使用r表示目前可以表示的右边界,如果当前值 > r, 超出范围,又因为 [1, n] 区间内的任何数字都可以用 nums 中某几个数字的和来表示,那么只需要有n/2以及 [1, n/2] 区间内任何数字都可以用 nums 中某几个数字的和来表示即可。所有我们将r扩大一倍,继续判断是否满足。
  3. 直到 r >= n。
int minPatches(vector<int> &nums, int n) {
    sort(nums.begin(), nums.end());
    long long r = 1;
    int i = 0;
    int cnt = 0;
    while(r <= n) {
        if(i < nums.size() && nums[i] <= r) r += nums[i++];
        else {
            cnt ++;
            r *= 2;
        }
    }
    return cnt;
}

7. 买卖股票的最佳时机

题目: 假设有一个数组,它的第i个元素是一支给定的股票在第i天的价格。如果你最多只允许完成一次交易(例如,一次买卖股票),设计一个算法来找出最大利润。
分析: 先低价买入,再高价卖出,因此从前向后,记录最小值并且更新最有结果,

int maxProfit(vector<int> &prices) {
    int minp = prices[0];
    int ans = 0;
    for(int i = 1; i < prices.size(); i ++) {
        ans = max(ans, prices[i] - minp);
        minp = min(minp, prices[i]);
    }
    return ans;
}

8. 买卖股票的最佳时机II

题目: 假设有一个数组,它的第i个元素是一个给定的股票在第i天的价格。设计一个算法来找到最大的利润。你可以完成尽可能多的交易(多次买卖股票)。然而,你不能同时参与多个交易(你必须在再次购买前出售股票)。
分析: 多次买卖,我们可以尽可能多的买卖股票,如果满足prices[i+1] > price[i],就进行一次买卖,其实我们知道如果是一个递增序列,(prices[i+1] - prices[i]) + (prices[i] - prices[i-1]) = prices[i+1] - prices[i],可以保证我们将所有可能的买卖识别出来。

int maxProfit(vector<int> &prices) {
    int sum = 0;
    for(int i=1;i<prices.size();i++){
        if(prices[i] > prices[i-1]){
            sum += prices[i] - prices[i-1];
        }
    }
    return sum;
}

9. 买卖股票的最佳时机含手续费

题目: 给定一个数组,其中第i个元素是一支股票在第i天的价格,以及一个非负数 fee 代表了交易手续费。(只需要在卖出时支付 fee)。你可以进行任意次交易,而每次交易都必须付手续费,而且你不能持有超过1支数量的股票(也就是说,你在买入之前需要先把之前买入的卖出)。返回可以获得的最大利润。
分析:

  • 我们考虑最朴素的方法,对于每一天,如果当前有股票,考虑出售或者保留,如果没股票,考虑购买或者跳过,进行dfs搜索。每天都有两种操作,时间复杂度为O(2^n).
  • 如何优化呢?我们用动态规划的思想来解决这个问题,考虑每一天同时维护两种状态:拥有股票(own)状态和已经售出股票(sell)状态。用own和sell分别保留这两种状态到目前为止所拥有的最大利润。 对于sell,用前一天own状态转移,比较卖出持有股是否能得到更多的利润,即sell = max(sell , own + price - fee), 而对于own , 我们考虑是否买新的股票更能赚钱(换言之,更优惠),own=max( own, sell-price).
  • 初始化我们要把sell设为0表示最初是sell状态且没有profit,把own设为负无穷因为最初不存在该状态,我们不希望从这个状态进行转移.
  • 因为我们保存的都是最优状态,所以在买卖股票时候取max能保证最优性不变.
  • 最后直接返回sell即可.
  • 来自(https://www.jiuzhang.com/solution/best-time-to-buy-and-sell-stock-with-transaction-fee/#tag-highlight-lang-cpp)
int maxProfit(vector<int> &prices, int fee) {
    int sell = 0, buy = -prices[0];
    for (int price : prices) {
        int sellOld = sell;
        sell = max(sell, buy + price - fee);
        buy = max(buy, sellOld - price);
    }
    return sell;
}

10. 最后的猫

题目: 给你一个n只猫,每一个猫都有一个初始化的萌系数,当一只猫的萌系数变成0它就会离开你。现在你实在受不了这n只萌猫,想要只留下一只猫,并且使它的萌系数最低。每一个你可以选择任意一只猫A去消耗另外一只猫B的萌系数,这样的话猫B的萌系数就会减去猫A的萌系数,当猫A的萌系数不变。通过多次回合之后,最后剩下的猫的萌系数最小是多少。
分析: 我们的目的是留下一只猫,使它的萌系数最小,从这个角度出发,我们可以选择最小萌系数的猫,去消耗其他的猫,如果其他的猫萌系数变成0,就离开了。例如最小萌系数的猫的系数是a,对于其他的猫,如果b%a == 0,则经过多次消耗之后,b就会离开,如果b%a!=0, 则结果是经过多轮消耗之后变成(b%a, a),直到一方变成0,我们可以发现这是一个求最大公约的算式。因此,最后的猫萌系数是gcd(h[0],h[0],…,h[n-1]);

int gcd(int a, int b) {
    if(a == 0) return b;
    return gcd(b % a, a);
}
int solve(vector<int> &h) {
    if(h.size() == 1) return h[0];
    int ans = gcd(h[0], h[1]);
    for(int i = 2; i < h.size(); i ++) {
        ans = gcd(ans, h[i]);
    }
    return ans;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/451549.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

I2C驱动AT24C02

文章目录 一、硬件电路设备地址 二、使用步骤字节写:页写入:任意写:任意读: 一、硬件电路 设备地址 设备需要一个8位的设备地址字&#xff0c;后面跟着一个启动条件&#xff0c;以使芯片能够进行读或写操作 设备地址字由一个强制的1,0序列的前四个最有效的位&#xff0c;如所示…

嵌入式面试收到了两个offer,一个单片机开发,一个Linux开发,不知道如何选择?

今天看到一个问题&#xff1a; 如果你想真正解决选择困难症的问题&#xff0c;请花几分钟&#xff0c;看完这篇内容。 我做了单片机开发多年&#xff0c;还是有感而发的。 我职业生涯里&#xff0c;做过最错误的决定&#xff0c;就是哪个公司工资开的高&#xff0c;就去哪里。 …

数据结构·复杂度

目录 1 时间复杂度 2 大O渐进表示法 举例子&#xff08;计算时间复杂度为多少&#xff09; 3 空间复杂度 前言&#xff1a;复杂度分为时间复杂度和空间复杂度&#xff0c;两者是不同维度的&#xff0c;所以比较不会放在一起比较&#xff0c;但是时间复杂度和空间复杂度是用…

LeetCode 每日一题 Day 95-101

2917. 找出数组中的 K-or 值 给你一个整数数组 nums 和一个整数 k 。让我们通过扩展标准的按位或来介绍 K-or 操作。在 K-or 操作中&#xff0c;如果在 nums 中&#xff0c;至少存在 k 个元素的第 i 位值为 1 &#xff0c;那么 K-or 中的第 i 位的值是 1 。 返回 nums 的 K-o…

中医药专家学者齐聚丽江 把脉健康产业新发展

3月10日&#xff0c;中国丽江第二届健康产业发展论坛暨全国卫生健康技术推广传承应用项目技能大赛在丽江&#xff08;国际&#xff09;民族文化交流中心隆重开幕。国内生物科学、中医学领域的多名专家学者齐聚丽江&#xff0c;探讨健康产业的未来发展趋势&#xff0c;为丽江乃至…

C/C++语言学习基础版(一)

目录 一and二、C语言说明 注释&#xff1a; 1、声明语句 2、输出函数 3、return 语句 三、C语言的数据结构 1、常量与变量 2、基本数据结构 3、关键字 练习&#xff1a;进制转换 四、基本输入输出 1、字符输出函数putchar 2、字符输入函数getchar 3、格式化输出函…

在家不无聊,赚钱有门道:5个正规线上赚钱平台,轻松开启副业

随着网络技术的快速发展&#xff0c;越来越多的人开始寻求通过网络来探索兼职副业的可能性&#xff0c;期望实现额外的收入。在这个过程中&#xff0c;选择一个正规且可靠的线上兼职平台显得尤为关键。 为此小编精心网上盘点了5个正规且靠谱的线上兼职副业平台。这些平台不仅安…

C语言深入理解指针(1)

前言 小陈也是学完了指针&#xff0c;还是有很多不多的地方&#xff0c;接下来会输出5篇博客去帮助自己彻底弄懂指针&#xff0c;以前的知识也需要复盘了呀。 内存和地址 1.1 内存 举个例子&#xff0c;去理解这两个的词&#xff0c;一个外卖员去送外卖&#xff0c;他首先需…

Halcon 使用光流算子检测运动物体

文章目录 算子optical_flow_mg 计算两个图像之间的光流vector_field_length 计算向量场的向量长度select_shape_std 选择给定形状的区域vector_field_to_real 将矢量场图像转换为两个实值图像intensity 计算灰度值的均值和偏差local_max_sub_pix 以亚像素精度检测局部极大值 Ha…

手撸nano-gpt

nano GPT 跟着youtube上AndrejKarpathy大佬复现一个简单GPT 1.数据集准备 很小的莎士比亚数据集 wget https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt 1.1简单的tokenize 数据和等下的模型较简单&#xff0c;所以这里用了个…

飞塔防火墙开局百篇——002.FortiGate上网配置——在路由模式下使用虚拟接口对(virtual-wire-pair)

在路由模式下使用虚拟接口对&#xff08;virtual-wire-pair&#xff09; 拓扑配置接口配置策略 使用方有透明模式下一进一出的这样需求的组网&#xff0c;可以在路由模式下使用虚拟接口对&#xff08;virtual-wire-pair&#xff09;替代。 登陆FortiGate防火墙界面&#xff0c;…

城市基础信息管理系统 (VB版电子地图源码/公交车线路图/超市平面图)-143-(代码+程序说明)

转载地址http://www.3q2008.com/soft/search.asp?keyword143 请访问 以下地址,查看最新版本, 新增加支持 建筑物 距离测量, 鸟瞰, 地图放大缩小, VB完善地图扩充程序(城市街道基础信息管理系统 )-362-&#xff08;代码&#xff0b;&#xff09; 这套系统印象深刻 因为,写了一…

12双体系Java学习之局部变量和作用域

局部变量 局部变量的作用域 参数变量

数据结构中的堆(Java)

文章目录 把普通数组转换大顶堆数组堆增删改查替换堆排序 把普通数组转换大顶堆数组 该方式适用索引为0起点的堆 在堆&#xff08;Heap&#xff09;这种数据结构中&#xff0c;节点被分为两类&#xff1a;叶子节点&#xff08;Leaf Nodes&#xff09;和非叶子节点&#xff08;N…

面试旺季,鸿蒙开发岗位怎么能没有面试题刷呢?

一年一度的面试浪潮来袭&#xff0c;你是否也想着利用这次机会去实现&#xff0c;跳槽涨薪的梦呢&#xff1f;在往年这个时候基本就有许多的小伙伴跑找到我要相关的面试题进行刷题&#xff0c;或要简历模板对自己的简历进行优化。 今年我又整了点新鲜的面试题&#xff0c;如果…

Linux系统之ipcalc命令的基本使用

Linux系统之ipcalc命令的基本使用 一、ipcalc命令介绍二、ipcalc命令的使用帮助2.1 ipcalc命令的help帮助信息2.2 ipcalc命令的语法解释 三、ipcalc命令的基本使用3.1 计算子网掩码3.2 计算网络地址3.3 找出所对应的主机名3.4 计算子网详细信息 四、ipcalc命令使用注意事项 一、…

由于 Positive Technologies 的专业知识,Moxa 消除了工业无线转换器中的一个漏洞。

我们的专家在 NPort W2150A 和 W2250A 转换器中发现了该漏洞 - 这些设备可将工业控制器、仪表和传感器连接到本地 Wi-Fi 网络。Moxa 已根据负责任的披露政策通知了该威胁&#xff0c;并发布了软件更新。 &#x1f977; 攻击者可以完全访问这些设备。 Positive Technologies 公…

目标检测应用场景—数据集【NO.28】无人机红外目标检测数据集

写在前面&#xff1a;数据集对应应用场景&#xff0c;不同的应用场景有不同的检测难点以及对应改进方法&#xff0c;本系列整理汇总领域内的数据集&#xff0c;方便大家下载数据集&#xff0c;若无法下载可关注后私信领取。关注免费领取整理好的数据集资料&#xff01;今天分享…

(二)运行自己的stable-diffusion

前面的步骤如https://datawhaler.feishu.cn/docx/BwjzdQPJRonFh8xeiSOcRUI3n8b所示 拷贝、解压文件后&#xff0c;进入到stable-diffusion-webui的文件夹中&#xff0c;文件如下&#xff1a; 启动&#xff1a; 运行效果&#xff1a; 由于生成了好几个图&#xff0c;所以…

为什么不要使用elasticsearch

互联网上有很多文章&#xff0c;都在讲为什么要使用elasticsearch&#xff0c;却很少有人讲为什么不要使用elasticsearch。作为深入研究elasticsearch四年&#xff0c;负责公司万亿级别检索的操盘手&#xff0c;借着这篇文章&#xff0c;给大家分享一下&#xff0c;为什么不要使…