贪心法
会议安排问题
问题描述:
- 假设有n个会议,1个会议室,其中每个会议都要求使用会议室,而在同一时间内只能有一个会议使用该会议室,每个会议都提供使用该会议室的起始时间和结束时间,如果某个会议使用了该会议室,则它在半开区间[开始时间,结束时间)占用该会议室,如何选择会议进行顺序确保资源的最大化利用?
贪心策略:
- 选择开始时间最早且使用时间最短的会议
会议结束时间=会议开始时间+使用资源时间,即: 每次从剩下未安排的会议中选择具有最早结束时间且不会与已安排的会议重叠的会议
java语言设计步骤:
- 1.每个会议都有自己的编号,开始时间和结束时间,可以构建一个会议类,然后创建会议类型的对象
- 2.根据会议的结束时间的大小进行升序排列
特殊情况1: 如果结束时间相同的话,按开始时间升序排列
特殊情况2: 会议排序后的最后一个会议并不满足条件,需要记录满足条件的最后一个会议的下标- 3.从第一个会议开始,从左到右按顺序选择不会与已安排的会议重叠的会议
Java代码:
import java.util.Scanner;
class Meet{
private int Sign;//会议编号
private int Start;//会议开始时间
private int End;//会议结束时间
//构造函数
public Meet(int Sign,int Start,int End) {
this.Sign = Sign;
this.Start = Start;
this.End = End;
}
//设置与返回
public void setSign(int Sign) {
this.Sign = Sign;
}
public int getSign() {
return Sign;
}
public void setStart(int Start) {
this.Start = Start;
}
public int getStart() {
return Start;
}
public void setEnd(int End) {
this.End = End;
}
public int getEnd() {
return End;
}
}
class MeetArrangeApp{
private int count;//数组大小
private Meet[] Meeting;//会议类型的数组
Scanner scanner = new Scanner(System.in);//输入流
//构造函数
public MeetArrangeApp(int count) {
this.count = count;
Meeting = new Meet[count];
}
//添加会议
public void Add() {
for(int i = 0;i < count;i++) {
System.out.println("请输入会议" + (i+1) + "的会议编号,开始时间和结束时间:");
Meeting[i] = new Meet(scanner.nextInt(),scanner.nextInt(),scanner.nextInt());
}
}
//展示添加的会议
public void displayMeet() {
System.out.print("会议编号:");
for(int i = 0;i < count;i++) {
System.out.print(Meeting[i].getSign() + " ");
}
System.out.println("");
System.out.print("开始时间:");
for(int i = 0;i < count;i++) {
System.out.print(Meeting[i].getStart() + " ");
}
System.out.println("");
System.out.print("结束时间:");
for(int i = 0;i < count;i++) {
System.out.print(Meeting[i].getEnd()+ " ");
}
System.out.println("");
}
//贪心策略
public void selectMeet() {
int out,in,min;
//首先给会议按结束时间排序,使用选择排序;可以优化,选择其他排序方式
for(out = 0; out < count - 1;out++) {
min = out;
for(in = out + 1;in < count;in++) {
if(Meeting[in].getEnd() < Meeting[min].getEnd()) {
min = in;
}
}
swap(out,min);
}
//特殊情况:结束时间相等,但是前一个开始时间比后一个开始时间大,按照开始时间重新升序排列
for(int i = 0;i < count - 1;i++) {
if(Meeting[i].getEnd() == Meeting[i+1].getEnd() && Meeting[i].getStart() > Meeting[i+1].getStart()) {
swap(i,i+1);
}
}
}
//交换数据
public void swap(int one,int two) {
Meet temp;
temp = Meeting[one];
Meeting[one] = Meeting[two];
Meeting[two] = temp;
}
//选择会议
public void findMeet() {
System.out.println("选择第" + Meeting[0].getSign() + "号会议");
int out = 0;
int in = 0;
//特殊情况:排序后会议的最后一个会议并不满足条件,需要记录满足条件的最后一个会议的下标
int record = 1;
for(out = 0;out < count - 1;out++) {
for(in = out + 1;in < count;in++) {
if(Meeting[out].getEnd() <= Meeting[in].getStart()) {
record++;
System.out.println("选择第" + Meeting[in].getSign() + "号会议");
out = in;
}
}
}
System.out.println("结束时间Last = " + Meeting[record].getEnd());
}
}
public class MeetArrange {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int count;
System.out.println("请输入需要安排的会议的个数:");
count = scanner.nextInt();
MeetArrangeApp maa = new MeetArrangeApp(count);
maa.Add();
maa.displayMeet();
System.out.println("--------------------------------//首先把会议按结束时间从小到大排序");
maa.selectMeet();
maa.displayMeet();
System.out.println("--------------------------------//然后根据贪心法选择会议");
maa.findMeet();
scanner.close();
}
}
C++语言设计步骤:
- 1.初始化,将n个会议的开始时间存储在数组B中;将n个会议的结束时间存储在数组E中且按照结束时间的非减序排序,数组B需要做相应调整; 采用集合A来存储问题的解,如果会议i在集合A中,当且仅当A[i]= true。
2.根据贪心策略,算法首先选择会议1,即令A[1]= true。依次扫描每一个会议,如果会议i的开始时间不小于最后一个选入集合A中的会议的结束时间,即会议i与A中会议相容,则将会议i加入集合A中;否则,放弃会议i,继续检查下一个会议与集合A中会议的相容性
C++代码:
核心代码:
void GreedySelector(){
E中元素按非减序排列,B中对应元素做相应调整;
int i,j;
//初始化选择会议的集合A, 即只包含会议1
A[1] = true;
j = 1;i=2;
//从会议i开始寻找与会议j相容的会议
while(i <= n){
//会议i的开始时间不小于最后一个选入集合A中的会议的结束时间
if(B[i] >= E){
//将会议i加入集合A中
A[i] = true;
j = i;
}else{
A[i] = false;
}
}
}
算法分析:
该算法的时间主要消耗在将各个会议按束时间从小到大进行排序的操作
- 时间复杂度: 若采用快速排序算法进行排序,算法的时间复杂性O(nlogn)。也可以采用其他方法
- 空间复杂度: 该算法的空间复杂性是常数阶,即S(n)= 0(1)。