Greedy算法基本思想:
贪心算法是一种在每一步选择中都采取在当前状态下最好或最优的选择,从而希望结果是最好或最优的结果。
希望通过做出局部优化选择达到全局优化选择。
算法不一定总产生优化解。
Greedy算法产生优化解的条件:
具有优化子结构;
具有贪心选择性;
优化子结构:若一个优化问题的优化解包含它的(剩余)子问题的优化解,则称其具有优化子结构。
贪心选择性:一个优化问题的全局优化解可以通过局部优化选择得到。
贪心算法的求解过程
(1)候选集合A:为了构造问题的解决方案,有一个候选集合A作为问题的可行解,即问题的最终解均取自候选集合A。
(2)解集合S:随着贪心选择的进行,解集合S不断扩展,直到构成满足问题的完整解。
(3)解决函数solution:检查解集合S是否构成问题的完整解。
(4)选择函数select:即贪心策略,这是关键,指出哪个候选对象最有希望构成问题的解,通常和目标函数有关。
(5)可行函数feasible:检查解集合中加入一个候选对象是否可行,即解集合扩展后是否满足约束条件。
贪心算法的一般流程:
Greedy(A)
{
S = {}; //初始集合为空集
while(not solution(S)) //集合S没有构成问题的一个解
{
x = select(A); //在候选集合A中做贪心选择
if (feasible(S,x)) //判断集合S中加入x后的解是否可行
{
S = S + {x};
A = A - {x};
}
}
return S;
}
下面举几个例子:
1、最大连续子数组和:在一个整数数组中,求和最大的子数组的值。
#include<iostream>
#include<vector>
#include<string>
using namespace std;
/*最大连续子数组和*/
int MaxSubarray(vector<int>& vec)
{
int sum = vec[0];//最大值
int curmax = vec[0];//当前最大值
int i;
for(i=1;i<vec.size();i++)
{
curmax += vec[i];
if(curmax < 0)//当前和为负数,重新计数
curmax = 0;
if(curmax > sum)
sum = curmax;
}
if(sum < 0)//若数组中均为负数,则寻找一个最大值返回
{
for(i=0;i<vec.size();i++)
if(sum < vec[i])
sum = vec[i];
}
return sum;
}
int main()
{
int array[]={-1,1,-3,4,-1,2,1,-5,4};
vector<int> vec(array,array+sizeof(array)/sizeof(int));
cout<<MaxSubarray(vec)<<endl;
}
2、分糖果,一排小朋友,保证每个小朋友至少有一个糖果,同时保证个子比相邻小朋友高的所分的糖果要比他的邻居多,只要需要多少糖果。
#include<iostream>
#include<vector>
using namespace std;
/*进行两次扫描,一次从左向右,一次从右向左,第一次扫描的时候维护对于每一个小孩
左边所需要的最少的糖果数量,存入数组,第二次维护右边所需的最少的糖果数量
并且比较将左边和右边的糖果数量存入结果数组对应元素中*/
int candy(vector<int>& ratings)
{
vector<int> candy(ratings.size(),1);
int sum,i;
for(i=1;i<ratings.size();i++)
{
if(ratings[i]>ratings[i-1])
candy[i]=candy[i-1]+1;
}
sum = candy[ratings.size()-1];
for(i=ratings.size()-2;i>=0;i--)
{
int cur = 1;
if(ratings[i] > ratings[i+1])
cur = candy[i+1]+1;
sum += max(cur,candy[i]);
candy[i] = cur;
/*
if(ratings[i]>ratings[i+1] && candy[i]<=candy[i+1])
candy[i] = candy[i+1]+1;
sum += candy[i];
*/
}
return sum;
}
int main()
{
int array[]={4,2,6,8,5};
vector<int> vec(array,array+sizeof(array)/sizeof(int));
cout<<candy(vec)<<endl;
}
3、跳远游戏,给定一个整数数组,数组中的元素代表在当前位置能够向前跳的最远距离,判断给定的这个跳远策略能否跳到最后的位置。
<span style="font-size:14px;">#include<iostream>
#include<vector>
using namespace std;
/*时刻计算当前位置和当前位置能跳的最远长度,并始终和界限比较
若在任意位置出现在最远跳步为0,那么就无法继续跳下去,
在任意位置出现最大跳步+当前位置>界限,那么就说明可以跳下去*/
bool canJump(vector<int>& vec)
{
if(vec.size()<=0)
return true;
int maxstep = vec[0];
for(int i=1;i<vec.size();i++)
{
if(maxstep == 0)
return false;
maxstep--;
if(maxstep < vec[i])
maxstep = vec[i];
if(maxstep+i >= vec.size()-1)
return true;
}
}
int main()
{
int array[]={2,3,1,1,4};
vector<int> vec(array,array+sizeof(array)/sizeof(int));
cout<<canJump(vec)<<endl;
}</span>