(学习10)贪心算法之相容问题
问题描述
相容问题:有一个礼堂需要用于举办活动,每个活动有开始时间与结束时间;如果任何两个活动不能在礼堂中同时进行,问如何选择这些活动,使得能在礼堂中安排最多活动;
解析:
贪心策略1:活动按照结束时间进行升序排序:
从第一个活动开始向后选择,若当前活动与前一个活动相容(fi-1<=si)
活动序号 i |
1 |
2 |
3 |
4 |
5 |
6 |
开始时间si |
1 |
2 |
2 |
4 |
6 |
8 |
结束世界fi |
2 |
3 |
4 |
6 |
12 |
12 |
选择活动1:这是结束最早的活动
选择活动2:因为s2==f1,与活动1相容,即纳入安排
不选择活动3:因为s3<f2,与活动2不相容,所以不纳入安排
选择活动4:因为s4>f2,与活动2相容,所以纳入安排
选择活动5:因为s5>f4,与活动4相容,所以纳入安排
不选择活动6:因为s6<f5,与活动5不相容,所以不纳入安排
策略一在这个问题上被证明是可行正确的,具体证明较为复杂,可以自行研究
贪心策略2:按照开始时间进行排序:
活动序号 i |
1 |
2 |
3 |
4 |
5 |
6 |
开始时间si |
1 |
2 |
2 |
4 |
6 |
8 |
结束世界fi |
2 |
3 |
4 |
6 |
12 |
12 |
选择活动1:这是结束最早的活动
选择活动2:因为s2==f1,与活动1相容,即纳入安排
不选择活动3:因为s3<f2,与活动2不相容,所以不纳入安排
选择活动4:因为s4>f2,与活动2相容,所以纳入安排
选择活动5:因为s5>f4,与活动4相容,所以纳入安排
不选择活动6:因为s6<f5,与活动5不相容,所以不纳入安排
反例:
活动序号 i |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
开始时间si |
1 |
1 |
2 |
2 |
4 |
5 |
6 |
结束世界fi |
2 |
3 |
3 |
4 |
7 |
6 |
8 |
而最优策略:
活动序号 i |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
开始时间si |
1 |
1 |
2 |
2 |
4 |
5 |
6 |
结束世界fi |
2 |
3 |
3 |
4 |
7 |
6 |
8 |
贪心策略3:按照活动时间进行排序:
活动序号 i |
1 |
2 |
3 |
4 |
5 |
6 |
开始时间si |
1 |
2 |
2 |
4 |
8 |
6 |
结束世界fi |
2 |
3 |
4 |
6 |
12 |
12 |
选择活动1:这是活动时间最少的活动
选择活动2:因为s2==f1,与活动1相容,即纳入安排
不选择活动3:因为s3<f2,与活动2不相容,所以不纳入安排
选择活动4:因为s4>f2,与活动2相容,所以纳入安排
选择活动5:因为s5>f4,与活动4相容,所以纳入安排
不选择活动6:因为s6<f5,与活动5不相容,所以不纳入安排
贪心策略3反例:
活动序号 i |
1 |
2 |
3 |
开始时间si |
7 |
4 |
8 |
结束世界fi |
9 |
8 |
14 |
而最优策略:
活动序号 i |
1 |
2 |
3 |
开始时间si |
7 |
4 |
8 |
结束世界fi |
9 |
8 |
14 |
针对策略一的伪代码设计
S={1};
//还需对活动按照结束时间进行升序排序 for (i=2;i<=n;i++){ if si>=fj{ append(S,i);//将该活动放到集合S中 j=i; } } return S;
复杂度分析:O(nlogn)
源代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// // main.cpp // 作业10 // // Created by yizhihenpidehou on 2020/4/28. // Copyright © 2020 yizhihenpidehou. All rights reserved. // #include <iostream> #include <algorithm> #include <stdio.h> #include <string.h> using namespace std; struct A{ int s;//开始时间 int e;//结束时间 }Act[200]; bool cmp(struct A a,struct A b){ return a.e<b.e; } void greedy(struct A* a,int n,int ap[]){ sort(a+1,a+1+n,cmp); ap[1]=1; int j=1;//记录最新纳入计划的活动的序号 int count=1;//记录有几个活动纳入计划 for(int i=2;i<=n;i++){ if(a[i].s>=a[j].e){//若当前活动与已纳入的最新活动兼容,则纳入计划 ap[++count]=i; j=i; } } for(int i=1;i<=count;i++){ printf("%d ",ap[i]); } printf("\n"); } int main(int argc, const char * argv[]) { Act[1].s=1;Act[1].e=2; Act[2].s=2;Act[2].e=3; Act[3].s=2;Act[3].e=4; Act[4].s=4;Act[4].e=6; Act[5].s=8;Act[5].e=12; Act[6].s=6;Act[6].e=12; int ap[200]={0}; greedy(Act,6,ap); return 0; }