Description
给出 n 个加工任务的开始时间和结束时间,问最少要用多少台机器可以完成这些任务,同一台机器上的任务时间不能有重叠,但允许守尾相接。
Input
第一行给出用例组数 T ,对于每组用例,第一行给出任务数量 n ,接下来 n 行,每行给出两个整数,表示任务的开始时间和结束时间。 1⩽T⩽100 , 1⩽n⩽105 。
Output
对于每组用例,输出两个整数,分别表示最少用的机器台数和所有机器用的总时间。一台机器的用时为它最后的结束时间减它最开始启动的时间,中间不会停机。
Sample Input
1
3
1 3
4 6
2 5
Sample Output
2 8
Solution
经典的任务分配问题。
直接贪心模拟。
根据每个零件的起始时间从小到大排序,维护一个multiset,把每个机器的结束时间放到set中,为什么用set,因为set基于红黑树可以实现自动排序,而且查找效率为O(logn),这样就不用自己手写二分了。然后扫一遍排序好的零件,从0~n-1,对于每一个零件的起始时间,在set中查找第一个大于等于的,如果没有,另开一台机器,使这个机器的开机时间为这个零件的开始时间,机器的关机时间为这个零件的结束时间。
如果set中存在大于等于零件起始时间的数,只要加上这个零件的结束时间,在减去这个机器的关机时间,最后再更新一下这个机器的关机时间就行了,关机时间等于这个零件的结束时间。
const int maxn=1e5+10;
struct node
{
int l,r;
bool operator<(const node &a)const
{
return l<a.l;
}
}s[maxn];
multiset<int>st;
int main()
{
ios::sync_with_stdio(false);
int T;
cin>>T;
while(T--)
{
st.clear();
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>s[i].l>>s[i].r;
int ans=0;
ll res=0;
sort(s,s+n);
for(int i=0;i<n;i++)
{
auto it=st.upper_bound(s[i].l);//寻找>=s[i].l的r
if(it==st.begin())//再开一个机器
{
ans++;
res+=s[i].r-s[i].l;
st.insert(s[i].r);
continue;
}
it--;//这里是离该i区间的起始点最近的结束点
res+=s[i].r-*it;//把这个区间的结束点接上上个结束点,不需要注意起点在哪
st.erase(it);//删掉当前区间结束点
st.insert(s[i].r);//重新更新
}
cout<<ans<<' '<<res<<endl;
}
return 0;
}