算法沉淀——贪心算法一(leetcode真题剖析)
算法沉淀——贪心算法一
- 01.柠檬水找零
- 02.将数组和减半的最少操作次数
- 03.最大数
- 04.���动序列
贪心算法(Greedy Algorithm)是一种基于贪心策略的优化算法,它通常用于求解最优化问题,每一步都选择当前状态下的最优解,以期望通过局部最优的选择最终达到全局最优。贪心算法的思想是在每一步都做出在当前状态下局部最优的选择,而不考虑未来可能造成的影响。
在应用贪心算法时,通常需要满足两个基本要素:
- 最优子结构性质(Optimal Substructure): 问题的最优解包含了其子问题的最优解。这意味着通过解决子问题可以得到原问题的最优解。
- 贪心选择性质(Greedy-Choice Property): 在做每一步的选择时,选择当前状态下的最优解,即局部最优解。这样希望通过局部最优选择达到全局最优。
贪心算法适用于一些特定类型的问题,但并不适用于所有问题。它通常比动态规划算法更加高效,因为它不需要考虑所有可能的解,只需选择当前状态下的最优解。然而,贪心算法的局限性在于可能会错过全局最优解,因为它没有考虑未来的影响。
经典应用贪心算法的问题包括:
- 活动选择问题(Activity Selection Problem): 选择一组互不相交的活动,使得参与的活动数最大。
- 霍夫曼编码(Huffman Coding): 用变长编码表示不同字符,使得编码长度的加权和最小。
- 最小生成树问题(Minimum Spanning Tree Problem): 在一个连通图中找到一棵包含所有顶点的树,使得树上边的权值之和最小。
- 单源最短路径问题的Dijkstra算法: 在一个带权图中,从一个顶点出发,找到到达其他顶点的最短路径。
需要注意的是,贪心算法并不总是能够找到问题的最优解,因此在应用时需要仔细分析问题的性质,确定贪心选择是否能够保证得到全局最优解。
01.柠檬水找零
题目链接:https://leetcode.cn/problems/lemonade-change/
在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
注意,一开始你手头没有任何零钱。
给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。
示例 1:
输入:bills = [5,5,5,10,20] 输出:true 解释: 前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。 第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。 第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。 由于所有客户都得到了正确的找零,所以我们输出 true。
示例 2:
输入:bills = [5,5,10,10,20] 输出:false 解释: 前 2 位顾客那里,我们按顺序收取 2 张 5 美元的钞票。 对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。 对于最后一位顾客,我们无法退回 15 美元,因为我们现在只有两张 10 美元的钞票。 由于不是每位顾客都得到了正确的找零,所以答案是 false。
提示:
- 1
public:
bool lemonadeChange(vector
int five = 0, ten = 0;
for(int& x:bills){
if(x==5) five++;
else if(x==10){
if(five==0) return false;
five--;ten++;
}
else{
if(ten&&five){
ten--;five--;
}
else if(five=3) five-=3;
else return false;
}
}
return true;
}
};
h202.将数组和减半的最少操作次数/h2
p题目链接:https://leetcode.cn/problems/minimum-operations-to-halve-array-sum//p
p给你一个正整数数组 nums 。每一次操作中,你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半。(注意,在后续操作中你可以对减半过的数继续执行操作)/p
p请你返回将 nums 数组和 至少 减少一半的 最少 操作数。/p
p示例 1:/p
pre class="brush:python;toolbar:false"输入:nums = [5,19,8,1]
输出:3
解释:初始 nums 的和为 5 + 19 + 8 + 1 = 33 。
以下是将数组和减少至少一半的一种方法:
选择数字 19 并减小为 9.5 。
选择数字 9.5 并减小为 4.75 。
选择数字 8 并减小为 4 。
最终数组为 [5, 4.75, 4, 1] ,和为 5 + 4.75 + 4 + 1 = 14.75 。
nums 的和减小了 33 - 14.75 = 18.25 ,减小的部分超过了初始数组和的一半,18.25 >= 33/2 = 16.5 。
我们需要 3 个操作实现题目要求,所以返回 3 。
可以证明,无法通过少于 3 个操作使数组和减少至少一半。
示例 2:
输入:nums = [3,8,20] 输出:3 解释:初始 nums 的和为 3 + 8 + 20 = 31 。 以下是将数组和减少至少一半的一种方法: 选择数字 20 并减小为 10 。 选择数字 10 并减小为 5 。 选择数字 3 并减小为 1.5 。 最终数组为 [1.5, 8, 5] ,和为 1.5 + 8 + 5 = 14.5 。 nums 的和减小了 31 - 14.5 = 16.5 ,减小的部分超过了初始数组和的一半, 16.5 >= 31/2 = 15.5 。 我们需要 3 个操作实现题目要求,所以返回 3 。 可以证明,无法通过少于 3 个操作使数组和减少至少一半。
提示:
- 1 public: int halveArray(vector priority_queue heap.push(x); sum+=x; } sum/=2.0; int count=0; while(sum0){ double tmp=heap.top()/2.0; heap.pop(); sum-=tmp; count++; heap.push(tmp); } return count; } }; public: string largestNumber(vector vectorreturn s1+s2s2+s1;}); string ret; for(string& s:strs) ret+=s; if(ret[0]=='0') return "0"; return ret; } }; public: int wiggleMaxLength(vector int n=nums.size(); if(n int right = nums[i+1]-nums[i]; if(right==0) continue; if(right*left
- 1
public:
bool lemonadeChange(vector
int five = 0, ten = 0;
for(int& x:bills){
if(x==5) five++;
else if(x==10){
if(five==0) return false;
five--;ten++;
}
else{
if(ten&&five){
ten--;five--;
}
else if(five=3) five-=3;
else return false;
}
}
return true;
}
};
h202.将数组和减半的最少操作次数/h2
p题目链接:https://leetcode.cn/problems/minimum-operations-to-halve-array-sum//p
p给你一个正整数数组 nums 。每一次操作中,你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半。(注意,在后续操作中你可以对减半过的数继续执行操作)/p
p请你返回将 nums 数组和 至少 减少一半的 最少 操作数。/p
p示例 1:/p
pre class="brush:python;toolbar:false"输入:nums = [5,19,8,1]
输出:3
解释:初始 nums 的和为 5 + 19 + 8 + 1 = 33 。
以下是将数组和减少至少一半的一种方法:
选择数字 19 并减小为 9.5 。
选择数字 9.5 并减小为 4.75 。
选择数字 8 并减小为 4 。
最终数组为 [5, 4.75, 4, 1] ,和为 5 + 4.75 + 4 + 1 = 14.75 。
nums 的和减小了 33 - 14.75 = 18.25 ,减小的部分超过了初始数组和的一半,18.25 >= 33/2 = 16.5 。
我们需要 3 个操作实现题目要求,所以返回 3 。
可以证明,无法通过少于 3 个操作使数组和减少至少一半。
The End