贪心算法
定义
在对问题求解时,总是做出在当前看来是最好的选择。不从整体最优上加以考虑,所做出的仅是在某种意义上的局部最优解。
贪心算法没有固定的算法框架,算法设计的关键是贪心策略的选择。必须注意的是,贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。
所以对所采用的贪心策略一定要仔细分析其是否满足无后效性。
基本思路
1.建立数学模型来描述问题。
2.把求解的问题分成若干个子问题。
3.对每一子问题求解,得到子问题的局部最优解。
4.把子问题的解局部最优解合成原来解问题的一个解。
适用的问题
贪心策略适用的前提是:局部最优策略能导致产生全局最优解。实际上,贪心算法适用的情况很少。一般,对一个问题分析是否适用于贪心算法,可以先选择该问题下的几个实际数据进行分析,就可做出判断。
贪心策略的选择
因为用贪心算法只能通过解局部最优解的策略来达到全局最优解,因此,一定要注意判断问题是否适合采用贪心算法策略,找到的解是否一定是问题的最优解。
分治算法
定义
分治算法核心是:分而治之,就是将原问题划分成n个规模较小,并且和原问题相似的子问题,递归的去解决这些问题,然后将结果合并,最后得到原问题的答案。
分治算法的递归实现中,每一层递归都包含了这样三个操作:
- 分解:将原问题分解成一系列子问题。
- 解决:递归地求解各个子问题,若子问题足够小,则直接求解。
- 合并:将子问题的结果合并成原问题。
分治算法原则如下:
- 原问题和子问题使用相同的计算模式
- 子问题之间相互独立,不会相互影响
- 当子问题足够小,可以直接求解出答案
子问题解决后,还可以合并为原问题期望的结果,而且合并的时间复杂度要低于原问题直接解决的时间复杂度
(例)归并排序-分治算法
问题描述:
输入:待排序列r[n],待排区间[s,t]
输出:升序序列r[s]~r[t]
分析:
- 划分
- 求解子问题
- 合并
归并排序首先执行划分过程,直到子序列长度为1,再在回溯的过程中排序。在merge_()函数中,由于回溯回来的两个子序列已经有序,所以只需依次取出两者中最小值中的较小者即可。
#include <iostream>
using namespace std;
void Mergesort(int r[],int s,int t);
void merge_(int r[],int s,int m,int t);
int r[10010],r1[10010];
int main()
{
int n,i;
cin>>n;
for(i=0;i<n;i++)
cin>>r[i];
Mergesort(r,0,n-1);
for(i=0;i<n;i++)
cout<<r[i]<<" ";
cout<<endl;
return 0;
}
void Mergesort(int r[],int s,int t)
{
if(s == t)
return ;
else
{
int m = (s+t)/2;
Mergesort(r,s,m);
Mergesort(r,m+1,t);
merge_(r,s,m,t);
for(int i=s;i<=t;i++)
r[i] = r1[i];
}
}
void merge_(int r[],int s,int m,int t)
{
int i=s,j=m+1,k=s;
while(i<=m && j<=t)
{
if(r[i] <= r[j])
r1[k++]=r[i++];
else
r1[k++]=r[j++];
}
while(i<=m)
r1[k++]=r[i++];
while(j<=t)
r1[k++]=r[j++];
}
(二) 例题
导弹拦截
题目描述
某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统,但是这种拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,由于该系统还在试用阶段。所以一套系统有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度不大于30000的正整数)。计算要拦截所有导弹最小需要配备多少套这种导弹拦截系统。
输入
n颗依次飞来的高度(1≤n≤1000)
输出
要拦截所有导弹最小配备的系统数k
样例输入
389 207 155 300 299 170 158 65
样例输出
2
提示
输入:导弹高度: 7 9 6 8 5
输出:导弹拦截系统K=2
输入:导弹高度: 4 3 2
输出:导弹拦截系统K=1
#include<bits/stdc++.h>
using namespace std;
int s[1005];
int l[1005];
int main()
{
int n =1;
memset(s,0,sizeof(s));
memset(l,0,sizeof(s));
while(cin >> s[n])
{
n++;
}
int k = 1;
l[k] = s[1];
int p = 0;
for(int i = 2;i <n ;i++){
p=0;
for(int j = 1;j <=k;j++){
if(l[j] >= s[i]){
if(p==0) p = j;
else if(l[j] < l[p]) p = j;
}
}
if(p==0){
k++;
l[k] = s[i];
}
else{
l[p] = s[i];
}
}
cout << k << endl;
}
活动选择
题目描述
学校在最近几天有n个活动,这些活动都需要使用学校的大礼堂,在同一时间,礼堂只能被一个活动使。由于有些活动时间上有冲突,学校办公室人员只好让一些活动放弃使用礼堂而使用其他教室。
现在给出n个活动使用礼堂的起始时间bi和结束时间ei(bi < ei<=32767),请你帮助办公室人员安排一些活动来使用礼堂,要求安排的活动尽量多。
输入导弹依次飞来的高度(雷达给出的高度不大于30000的正整数)。计算要拦截所有导弹最小需要配备多少套这种导弹拦截系统。
输入
第一行一个整数n(n<=1000); 接下来的n行,每行两个整数,第一个bi,第二个是ei(bi < ei<=32767)
输出
输出最多能安排的活动个数
样例输入
11
3 5
1 4
12 14
8 12
0 6
8 11
6 10
5 7
3 8
5 9
2 13
样例输出
4
#include<bits/stdc++.h>
using namespace std;
const int MAXN