系列文章目录
面向问题求解的高级程序设计:
(一)枚举
(二)查找
(三)贪心算法
(四)搜索
(五)分治与递归
(六)动态规划
(七)最短路
(八)最小生成树
(九)数论
(十)期中回顾
文章目录
一、贪心算法
- 贪心,在现实中的意思是“贪得无厌,不知足”,代表一种只顾眼前利益,不顾及长远局面的行为。在一般情况下,“贪心”是个贬义词。
- 贪心算法,有些教材上又称为贪婪算法,正是采纳了“贪心”中的“只顾眼前”这一点。贪心算法往往用来解决最优化问题,即求在一定约束下的全局最优解。因为对局部进行处理往往比长远考虑要容易的多。而且在一定的问题中,“只顾眼前”反而能得到“长远最优”,此时使用贪心算法来求解就非常方便。
- 换一句话讲,贪心算法就是把问题的求解分为好多步,在每一步内取当前的局部最优,并且最终能够通过这些局部的最优解达到全局最优。
因为贪心算法无论是构造还是实现都非常简单,因此在解一道题时往往都会优先想到一个或者多个贪心算法。因为贪心算法的“简单”,导致其在比赛中频频出现,时不时给出题人或选手带来惊喜,并且也留下了许多传颂千古的名言警句。
正确性检验
- 贪心算法的正确性,指的是一个贪心算法能否通过局部最优最终推得全局最优。贪心算法虽然在每一步只用考虑当前步骤,处理起来非常简单。然而,如果不证明正确性,我们就没法确定最终得到的解就是最优的。因此,在设计出一种贪心算法后,我们都需要证明其正确性。
然而,贪心算法的正确性证明非常困难。而且针对不同的问题,证明方法多种多样。 - 一种比较常用的证明思路为:
- 用贪心算法得到的所有局部最优解构造一个解的序列/集合(根据题目的解是否有序)
- 列举出其他所有可能的解的序列/集合
- 证明所有其他序列/集合的解都不优于贪心算法得到的解
贪心算法的优缺点
- 贪心算法的优势
1.算法思想比较简洁、直观
2.只用考虑局部情况,实现非常简单
3.复杂度较优,效率较高 - 贪心算法的劣势
1.正确性证明非常困难
2.证伪时的反例构造比较困难
3.有些贪心题的结论很难想到
二、例题
1.Yogurt factory
参考文章:Poj 2393 Yogurt factory【贪心】
2.Regular Bracket Sequence
参考文章:A. Regular Bracket Sequence(思维)
3. 今年暑假不AC
参考文章:杭电-今年暑假不AC
4. Sasha and Magnetic Machines
参考文章:Codeforces 1113B. Sasha and Magnetic Machines 题解
5. Serval and Parenthesis Sequence
参考文章:C. Serval and Parenthesis Sequence(合法括号序列)
6. Crossing River
#include <iostream>
#include <algorithm>
using namespace std;
int cmp(int a,int b)
{
return a<b;
}
int a[1010]= {};
int main()
{
int T,n,t;
int i;
cin >> T;
while(T--)
{
t = 0;
cin >> n;
for(i=0; i<n; i++)
{
cin >>a[i];
}
sort(a,a+n,cmp);
if(n<=3)
{
if(n==1)t=a[0];
if(n==2)t=a[1];
if(n==3)t=a[2]+a[0]+a[1];
}
else
{
for(int i=n-1;i>0;i=i-2)
{
if(i>=3)
{
if(a[0]+a[i-1]>=2*a[1])
t=t+a[0]+2*a[1]+a[i];
else
t=t+2*a[0]+a[i]+a[i-1];
}
if(i==2)
t=t+a[0]+a[1]+a[2];
if(i==1)
t=t+a[1];
}
}
cout << t << endl;
}
return 0;
}
参考文章:poj-1700 crossing river(贪心题)