一、题目
1.原题
在某个项目中有多个任务(用 tasks 数组表示)需要您进行处理,
其中 tasks[i] = [si, ei],
你可以在 si <= day <= ei 中的任意一天处理该任务。
请返回你可以处理的最大任务数。
注:一天可以完成一个任务的处理。
[排序, 队列, 贪心]
2.题目理解
贪心算法:如果任务可以按照结束时间排序,并且后续任务的开始时间晚于或等于当前任务的结束时间,那么可以贪心地选择尽可能多的任务。
例如,平时购物找零钱时,为使找回的零钱的硬币数最少,不要求找零钱的所有方案,而是从最大面值的币种开始,按递减的顺序考虑各面额,先尽量用大面值的面额,当不足大面值时才去考虑下一个较小面值,这就是贪心算法 。
将任务按结束时间排序后:
eg:[1, 4][3, 5][2, 7][6, 9]
task[0]可选择时间有:{1,2,3,4};
task[1]可选择时间有:{3,4,5};
task[2]可选择时间有:{2,3,4,5,6,7};
task[3]可选择时间有:{6,7,8,9}。
完整时间片为:1-9:{1,2,3,4,5,6,7,8,9}
从结束时间最晚的task[3]开始抓取,
选取9后task[3]完成了;
再到了task[2]选取最大值7;
再到了task[1]选取最大值5;
再到了task[0]选取最大值4;
每次都选择最大的,确保前边的值选择空间更多!
二、思路与代码过程
1.思路
输入:
任务数量n
tasks[n]数组
输出:
可以处理的最大任务数
贪心算法
2.代码过程
①main函数
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入该项目所含有的任务数量n:");
int n = sc.nextInt();
System.out.println("请输入任务数组tasks:");
int [][] tasks = new int[n][2];
for (int i = 0; i < n; i++) {
tasks[i][0] = sc.nextInt();
tasks[i][1] = sc.nextInt();
}
int maxTake = TaskCount(tasks) ;
System.out.println("可以处理的最大任务数为:"+maxTake);
}
②TaskCount
private static int TaskCount(int[][] tasks) {
int canTake = 0 ;
/*1.找到AllCanChose数组*/
int min = Arrays.stream(tasks) // 将二维数组转换为流
.flatMapToInt(Arrays::stream) // 展平为一维流
.min() // 获取最小值
.orElseThrow(() -> new IllegalArgumentException("Array is empty"));
int max = Arrays.stream(tasks)
.flatMapToInt(Arrays::stream)
.max()
.orElseThrow(()->new IllegalArgumentException("Array is empty"));
Set<Integer> AllCanChose = new HashSet<>();//固定值
for (int i = min ;i<=max ;i++) {
AllCanChose.add(i);
}
/*2.对任务数组排序*/
int[][] sortedTasks = Arrays.stream(tasks)
.sorted(Comparator.comparing(a -> a[1]))
.toArray(int[][]::new);
/*3.将一个个任务数组拆分为时间点和所有可选择天数数组一起传给TakeOrNot函数进行处理*/
for (int i = sortedTasks.length-1; i >= 0; i--) {
ArrayList<Integer> partCanTake = new ArrayList<>();//随i改变,每一次都是一组新的
int start = sortedTasks[i][0];
int end = sortedTasks[i][1];
for (int j =start;j<=end;j++) {
partCanTake.add(j);
}
boolean takeOrnot = TakeOrNot(AllCanChose,partCanTake);
if (takeOrnot){
canTake++;
}
}
return canTake;
}
③TakeOrNot
/*判断当前任务是否可以接取,在哪一天接*/
private static boolean TakeOrNot(Set<Integer> allCanChose, ArrayList<Integer> partCanTake) {
boolean takeOrnot = false;
if (allCanChose.size() !=0) {
int deadline = partCanTake.get(partCanTake.size()-1);
//如果在可选天里边当前任务的deadline还在,就选取deadline
if (allCanChose.contains(deadline)) {
allCanChose.remove(deadline);
takeOrnot = true;
}
//如果deadline已经被前边一个选走了:eg; 1,2 1,2 2,4 3,4
//顺延到前一天,若前一天也被选取了,继续前一天,如果没有可以选的,该任务就做不了(递归)
else {
if (partCanTake.size()>1){
partCanTake.remove(partCanTake.size()-1);
takeOrnot = TakeOrNot(allCanChose, partCanTake);
} else if (partCanTake.size()==1){
return takeOrnot;
}
}
}else {
return takeOrnot;
}
return takeOrnot;
}
三、运行结果
1.运行截图
2.带数据分析运行结果
tasks数组的最小值为:1
tasks数组的最大值为:4
所有可以选择的天有:[1, 2, 3, 4]
按照结束时间排序后的任务组为:
[1, 2]
[1, 2]
[2, 4]
[3, 4]
当前处理任务为:[3, 4]
当前任务可选择的天数有:[3, 4]
partCanTake的最后一天还可以选择!
选走最后一天后,剩余可选天数为:[1, 2, 3]
当前处理任务为:[2, 4]
当前任务可选择的天数有:[2, 3, 4]
移除最后一天后可选的有:[2, 3]
partCanTake的最后一天还可以选择!
选走最后一天后,剩余可选天数为:[1, 2]
当前处理任务为:[1, 2]
当前任务可选择的天数有:[1, 2]
partCanTake的最后一天还可以选择!
选走最后一天后,剩余可选天数为:[1]
当前处理任务为:[1, 2]
当前任务可选择的天数有:[1, 2]
移除最后一天后可选的有:[1]
partCanTake的最后一天还可以选择!
选走最后一天后,剩余可选天数为:[]
可以处理的最大任务数为:4
3.带数据分析完整代码
import javax.net.ssl.SSLContext;
import java.util.*;
public class test34 {
public static void main(String[] args) {
/*
Scanner sc = new Scanner(System.in);
System.out.println("请输入该项目所含有的任务数量n:");
int n = sc.nextInt();
System.out.println("请输入任务数组tasks:");
int [][] tasks = new int[n][2];
for (int i = 0; i < n; i++) {
tasks[i][0] = sc.nextInt();
tasks[i][1] = sc.nextInt();
}
for (int[] task : tasks) {
System.out.println(Arrays.toString(task));
}
*/
int [][] tasks = {{1,2},{1,2},{2,4},{3,4}};
int maxTake = TaskCount(tasks) ;
System.out.println("可以处理的最大任务数为:"+maxTake);
}
private static int TaskCount(int[][] tasks) {
int canTake = 0 ;
/*1.找到AllCanChose数组*/
int min = Arrays.stream(tasks) // 将二维数组转换为流
.flatMapToInt(Arrays::stream) // 展平为一维流
.min() // 获取最小值
.orElseThrow(() -> new IllegalArgumentException("Array is empty"));
int max = Arrays.stream(tasks)
.flatMapToInt(Arrays::stream)
.max()
.orElseThrow(()->new IllegalArgumentException("Array is empty"));
System.out.println("tasks数组的最小值为:" +min);
System.out.println("tasks数组的最大值为:" +max);
Set<Integer> AllCanChose = new HashSet<>();//固定值
for (int i = min ;i<=max ;i++) {
AllCanChose.add(i);
}
System.out.println("所有可以选择的天有:"+AllCanChose);
/*2.对任务数组排序*/
int[][] sortedTasks = Arrays.stream(tasks)
.sorted(Comparator.comparing(a -> a[1]))
.toArray(int[][]::new);
System.out.println("按照结束时间排序后的任务组为:");
for (int[] task : sortedTasks) {
System.out.println(Arrays.toString(task));
}
System.out.println();
/*3.将一个个任务数组拆分为时间点和所有可选择天数数组一起传给TakeOrNot函数进行处理*/
for (int i = sortedTasks.length-1; i >= 0; i--) {
System.out.println("当前处理任务为:"+Arrays.toString(sortedTasks[i]));
ArrayList<Integer> partCanTake = new ArrayList<>();//随i改变,每一次都是一组新的
int start = sortedTasks[i][0];
int end = sortedTasks[i][1];
for (int j =start;j<=end;j++) {
partCanTake.add(j);
}
System.out.println("当前任务可选择的天数有:"+partCanTake);
boolean takeOrnot = TakeOrNot(AllCanChose,partCanTake);
//System.out.print("每一次的判断:"+takeOrnot);
if (takeOrnot){
canTake++;
}
}
return canTake;
}
/*判断当前任务是否可以接取,在哪一天接*/
private static boolean TakeOrNot(Set<Integer> allCanChose, ArrayList<Integer> partCanTake) {
boolean takeOrnot = false;
if (allCanChose.size() !=0) {
int deadline = partCanTake.get(partCanTake.size()-1);
//如果在可选天里边当前任务的deadline还在,就选取deadline
if (allCanChose.contains(deadline)) {
System.out.println("partCanTake的最后一天还可以选择!");
allCanChose.remove(deadline);
System.out.println("选走最后一天后,剩余可选天数为:"+allCanChose);
System.out.println();
takeOrnot = true;
}
//如果deadline已经被前边一个选走了:eg; 1,2 1,2 2,4 3,4
//顺延到前一天,若前一天也被选取了,继续前一天,如果没有可以选的,该任务就做不了(递归)
else {
if (partCanTake.size()>1){
partCanTake.remove(partCanTake.size()-1);
System.out.println("移除最后一天后可选的有:"+partCanTake);
takeOrnot = TakeOrNot(allCanChose, partCanTake);
} else if (partCanTake.size()==1){
System.out.println("没有时间选啦,这个任务完成不了!");
}
}
}else {
System.out.println("可选时间已经没了,当前任务量就是最大的啦!");
}
return takeOrnot;
}
}