贪心算法思想:
顾名思义,贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。当然,希望贪心算法得到的最终结果也是整体最优的。虽然贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解。如单源最短路经问题,最小生成树问题等。在一些情况下,即使贪心算法不能得到整体最优解,其最终结果却是最优解的很好近似。
贪心算法的基本要素:
1、贪心选择性质。所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
(动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解.)
2、 当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。
贪心算法的基本思路:
从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。当达到算法中的某一步不能再继续前进时,算法停止。
该算法存在问题:
-
不能保证求得的最后解是最佳的;
-
不能用来求最大或最小解问题;
-
只能求满足某些约束条件的可行解的范围。
实现该算法的过程:
从问题的某一初始解出发;
while 能朝给定总目标前进一步 do
求出可行解的一个解元素;
由所有解元素组合成问题的一个可行解;
练习
1、分糖果问题
已知一些孩子和一些糖果,每个孩子有需求因子g,每个糖果有大小s,
当某个糖果的大小s>=某个孩子的需求因子g时,代表该糖果可以满足孩子;求使用这些糖果,最多能满足多少孩子?
(某个孩子只能用1个糖果满足)
例如:需求因子组g=[5,10,2,9,15,9];糖果大小数组s=[6,1,20,3,8];最多可以满足3个孩子。
算法思路:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int findcountchild(vector<int>&g,vector<int>&s)
{
sort(g.begin(),g.end());
sort(s.begin(),s.end());
int child=0;
int cookie=0;
while(child<g.size()&&cookie<s.size())
{
if(g[child]<s[cookie])
{
child++;
}
cookie++;
}
return child;
}
int main()
{
vector <int>g;
vector <int>s;
g.push_back(5);
g.push_back(10);
g.push_back(2);
g.push_back(9);
g.push_back(15);
g.push_back(9);
s.push_back(20);
s.push_back(3);
s.push_back(8);
int n=findcountchild(g,s);
cout << n << endl;
}
2、删除k个数字值最小问题
给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。
注意:
num 的长度小于 10002 且 ≥ k。
num 不会包含任何前导零。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-k-digits
示例一
输入: num = “1432219”, k = 3
输出: “1219”解释: 移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219
示例二
输入: num = “10200”, k = 1
输出: “200”
解释: 移掉首位的 1 剩下的数字为 200.注意输出不能有任何前导零。
示例三
输入: num = “10”, k = 2
输出: “0”
解释: 从原数字移除所有的数字,剩余为空就是0。
分析思路:
1、首先把当前给定的字符串表示的数字从高位开始逐个分离出来
2、设置一个栈,用于存放字符串分离出来的数字,从高到底逐个压入栈中。但是这里涉及到两个问题:栈是否为空、压入栈的数字是否是0
3、每分离出来一个数字,就进行一些判断:当前待入栈的数字和栈顶元素比较,如果栈顶元素大且K的值要大于0(保证移除指定个数的数字)且栈不为空就要先执行出栈操作然后再把这个待入栈元素入栈。
4、如果不满足3的情况,依然要考虑站是否为空的情况和待入栈的元素值是否为0的情况,因为当栈为空且待入栈的元素值为0的时候也不能入栈。除了这种情况之后其他情况都是可以入栈的。
5、如果是一个12345这样情况的数字,我们发现执行完3和4的情况之后K的值依然是大于0的。那么当上边的过程执行完毕我们还要单独判断K的值是否大于0,如果大于0的话,就要从栈顶删除若干个元素是k的值为0.
6、最后取出栈中元素组成字符串输出结果。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
string removenums(string num,int k)
{
vector<int> v;//用vector当作栈
string s=""; //用来存放最终的结果
for(int i=0;i<num.size();i++)
{
int n=num[i]-'0';
while(v.size()!=0&&k>0&&v[v.size()-1]>n)//
{
k--;
v.pop_back();
}
//n的值不是0就是正整数,所以n=0时需要满足v不是空即可插入
if(v.size()!=0 || n!=0)
{
v.push_back(n);
}
}
//当字符串为123456时上一个while没有删除元素
while(k>0&&v.size()!=0)
{
v.pop_back();
}
//将最后结果存放到s中
for(int i=0;i<v.size();i++)
{
s.append(1,'0'+v[i]);
}
if(s=="")//
s="0";
return s;
}
int main()
{
string num="1432219";
int k=3;
string n= removenums(num,k);
cout << n << endl;
return 0;
}
3、跳跃问题I
给定一个非负整数数组,假定你的初始位置为数组第一个下标。
数组中的每个元素代表你在那个位置能够跳跃的最大长度。
请确认你是否能够跳跃到数组的最后一个下标。
例如:A = [2,3,1,1,4]A=[2,3,1,1,4] 能够跳跃到最后一个下标,输出true;
A = [3,2,1,0,4]A=[3,2,1,0,4] 不能跳跃到最后一个下标,输出false。
输入格式
第一行输入一个正整数 n(1 \leq n \leq 500)n(1≤n≤500),接下来的一行 nn 个整数,输入数组 A_iAi。
输出格式
如果能跳到最后一个下标,输出true,否则输出false。
样例输入
5
2 0 2 0 1
样例输出
true
思路:
#include <iostream>
#include <vector>
#include <string>
int a[10002];
using namespace std;
int jumpjudge(int*a,int n)
{
vector<int> index; //最远可达到的位置
for(int i=0;i<n;i++)
{
index.push_back(i+a[i]);//计算index数组
}
int jump=0;
int max_index=index[0];
while(jump<n&&jump<=max_index)
{
if(max_index<index[jump])
{
max_index=index[jump];
}
jump++;
}
if(jump==index.size())
{
return 1;
}
return 0;
}
int main()
{
int n;
cin >>n;
for(int i=0;i<n;i++)
cin >> a[i];
if(jumpjudge(a,n))
cout << "true"<< endl;
else cout << "false" << endl;
return 0;
}
4、跳跃问题II
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例:
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
思路:
该题是leetcode 55 跳跃游戏的变形题
如果希望最少跳跃达到终点,则需明确何时进行跳跃是最合适的
贪心思想:
在到达某点前若一直不跳跃,发现从该点不能跳到更远的地方了,在这之前肯定有次必要的跳跃!
结论:在无法到达更远的地方时,在这之前应该跳到一个可以到达更远位置的位置!
#include <iostream>
#include <vector>
#include <string>
int a[10002];
using namespace std;
int jumpmin(int *a,int n)
{
if(n<2)
return 0;
int jump_min=1;
int current_max_index=a[0];//当前可以达到的最远距离
int pre_max_current_max_index=a[0];//在遍历当前可以达到的最远距离中最大的值
for(int i=0;i<n;i++)
{
if(i>current_max_index)
{
jump_min++;
current_max_index=pre_max_current_max_index;
}
if(a[i]>pre_max_current_max_index)
{
pre_max_current_max_index=a[i]+i;
}
}
return jump_min;
}
int main()
{
int n;
cin >>n;
for(int i=0;i<n;i++)
cin >> a[i];
cout << jumpmin(a,n) << endl;
return 0;
}
5、射击气球问题
思路:
#include <iostream>
#include <algorithm>
using namespace std;
bool judge_better(pair<int,int> val1,pair<int,int> val2){
return val1.first<val2.first;//只需将第一个元素排序即可
}
int NumsOfshoot(pair<int,int>*a,int n)
{
int shoot_num=1;
int shoot_begin=a[0].first;//射击起点
int shoot_end=a[0].second;//射击终点
sort(a,a+n,judge_better);
for(int i=1;i<n;i++)
{
if(a[i].first<=shoot_end)
{
shoot_begin=a[i].first;//缩小射击起点
if(a[i].second<shoot_end)
{
shoot_end=a[i].second;//缩小射击终点
}
}
else{
shoot_num++;
shoot_begin=a[i].first;
shoot_end=a[i].second;
}
}
return shoot_num;
}
int main()
{
pair<int,int> a[100];
int n;
cin >> n;
for(int i=0;i<n;i++)
{
cin >>a[i].first >>a[i].second;
}
int h=NumsOfshoot(a,n);
cout << h << endl;
return 0;
}
最优加油方法
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
bool judge_better(pair<int,int> val1,pair<int,int> val2){
return val1.first>val2.first;
}
int CppOilNums(int L,int P, vector<pair<int,int>>&a) //L为起始到终点的距离,P为开始时邮箱内的汽油量,pair<每个加油站到终点的距离,加油站可以加的油量>,n为加油站个数
{
priority_queue<int>Q; //存储油量的最大堆,该数据结构可以自动排序,从大到小
sort(a.begin(),a.end(),judge_better); //目的是将先后经过的加油站排序
int num=0; //记录加油的次数
a.push_back(make_pair(0,0)); //将终点作为最后一个停靠点
for(int i=0;i<a.size();i++)
{
int dis=L-a[i].first;
while(dis>P&&Q.empty()==false)
{
P+=Q.top();
Q.pop();
num++;
}
if(Q.empty()==true&&dis>P)
return -1;
P=P-dis;
L=a[i].first;
Q.push(a[i].second);
}
return num;
}
int main()
{
vector<pair<int,int>> a;
int n; //起点和终点在内的所有停靠点的数目
int L,P;//L为起始到终点的距离,P为开始时邮箱内的汽油量
cin >> n;
int distance,fuel;
for(int i=0;i<n;i++)
{
cin >> distance >>fuel;
a.push_back(make_pair(distance,fuel));
}
cin >> L >> P;
int count_point=CppOilNums(L,P,a);
cout << count_point << endl;
return 0;
}