题目来源:
HihoCoder1309
题目要求:
给定N项任务的起至时间(S1,E1), (S2, E2), ..., (SN, EN), 计算最少需要多少台机器才能按时完成所有任务。同一时间一台机器上最多进行一项任务,并且一项任务必须从头到尾保持在一台机器上进行。任务切换不需要时间。
解答:
本题比较简单。由于某些任务之间的时间具有交集,因此不能同时运行在同一台机器。基于这样的思路,只要找出所有任务中的“最大冲突子集”就可以得到答案,所谓“最大冲突子集”是指从所有的任务中选择部分任务构成一个子集,而在该子集中的任意两个任务都不能运行在同一台机器。得到最大冲突子集后,需要的机器数目就是最大冲突子集包含的任务的数目。这个结论是显而易见的,可以按照以下的思路验证其正确性:首先不论如何分配任务,处于同一个最大冲突子集的任务一定不能安排在同一台机器中,假设计算得到的最大冲突子集记作M,容量为K,并将这个K个任务记作m[1],m[2], ...m[K],此时对于子集中的K个任务需要K台机器,每台机器安排一个任务,然后对剩余的N-K个任务进行分配,这里将这N-K个任务记作t[1], t[2], ...t[N-K],显然,对于剩余的N-K个任务中的每一个任务t[i],包含在S中的K个任务中一定存在一个任务m[j]和t[i]不会发生冲突,否则,如果t[i]和M中所有的任务都发生冲突,那么将t[i]假如到集合S中就可以得到容量更大的冲突子集,这与“M是最大冲突子集”相矛盾。因此,对于每一个任务t[i],在为其安排运行的机器时,一定不会使机器的数目增加。因此,最少需要的机器数就是最大冲突子集的容量K。
接下来的问题是如何找到所有任务的最大冲突子集。根据定义,最大冲突子集中的每一个任务都和子集中其他的所有任务存在冲突,因此,最大冲突子集中的所有任务时间区间的交集一定不为空。基于这样的思路,可以按照这样的思路寻找最大冲突子集:首先设定一个变量w来记录每一个时刻同时运行的任务数目,然后将所有的Si、Ei(1≤ix≤N)按递增排序,然后进行遍历,当遍历到一个数据为某个任务的开始时间时,表明当前时刻有新的任务开始,进而,当前正在运行的任务的数目增加,因此,将w加1,当遍历到一个数据为某个任务的结束时间时,表明当前是时刻有一项任务结束,进而当前正在运行的任务数目减少,将w减1,根据题意,由于任务切换没有冲突和时间消耗,因此,在排序过程中,如果两个数据数值相同,应将结束时间排在前面。最后,在遍历过程中w达到的最大值,就是最大冲突子集的容量,也就是本题的答案。
输入输出格式:
输入:第一行一个整数N,(1≤N≤100000),表示任务的数目。 以下N行每行两个整数Si,Ei,(0 ≤Si<Ei≤1000000000),表示任务的起止时间。
输出:
输出一个整数,表示最少的机器数目。
程序代码:
package hihocoder;
import java.util.Arrays;
import java.util.Scanner;
/**
* This is the ACM problem solving program for hihoCoder 1309.
*
* @version 2017-04-26
* @author Zhang Yufei.
*/
public class HihoCoder1309 {
/**
* Record the task time.
*/
private static class TaskTime implements Comparable<TaskTime> {
static final int START = 0;
static final int END = 1;
int time;
int tag;
@Override
public int compareTo(TaskTime o) {
if(time > o.time) {
return 1;
} else if(time < o.time) {
return -1;
} else {
if(tag == START) {
return 1;
} else {
return -1;
}
}
}
}
/**
* Input data.
*/
private static int N;
/**
* The task list.
*/
private static TaskTime[] times;
/**
* The main program.
*
* @param args
* The command-line parameters list.
*/
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
N = scan.nextInt();
times = new TaskTime[2 * N];
for (int i = 0; i < 2 * N; i += 2) {
times[i] = new TaskTime();
times[i].time = scan.nextInt();
times[i].tag = TaskTime.START;
times[i + 1] = new TaskTime();
times[i + 1].time = scan.nextInt();
times[i + 1].tag = TaskTime.END;
}
scan.close();
Arrays.sort(times);
int result = 0;
int ans = 0;
for(int i = 0; i < 2 * N; i++) {
if(times[i].tag == TaskTime.START) {
ans++;
} else {
ans--;
}
if(result < ans) {
result = ans;
}
}
System.out.println(result);
}
}