题意
给n个工作的开始时间和结束时间 每个工作需要用机器来完成 让我们给这n个工作规划机器 规划出尽可能少机器数目 以及计算最少的机器时间(机器时间为机器使用的最终结束时间减去开始时间)
分析
这道题其实就是一个工作的开始结束形成了一个线段 让我们求多个线段不交叉不重叠拼接拼到一个方向上 拼合后 求最终剩下的线段方案数 和工作时间
所以这道题可以看出一个性质 那就是每次拍工作时 时根据上一个工作的结束时间 然后再在后面的工作开始时间里选择一个最近的 排上去
如果剩下的工作开始时间都比当前机器的结束时间小 那么就重新开新的机器
我们可以从头开始枚举点 如果这个点的开始时间在所有机器的结束时间里能找到个最近的结束时间 表示可以续上 那么我们就把这个工作的结束时间代替那个机器的结束时间 那么如何保证能找到那个点呢?
我们就需要对开始时间排序 保证当前处理的点 比他早开始的一定处理过了 尽可能地让当前点的开始时间可以接上前面结束的工作
*所以得到思路
用set维护机器个数
set中的值表示开的机器个数
每次遍历新规划时 就是取这个规划的开始点 看能否插在其中 如果可以就用新的结束点代替可以
插入位置的结束点
set中存的是每个规划的终点 表示这个规划目前机器的结束时间 如果可以继续插入表示可以代替
上次的结束时间 更新为新的结束时间
注意对于每一个新点 我们对这个点的开始时间二分 有两种情况
1 这个点能找到插入位置也就是有插入下界 那么范围会是第一个元素到最后一个元素
2 这个点找不到插入位置 那么会返回begin() 表示这个时候要开新机器
最后 set中的数量就是机器的个数
过程中可以计算间隔
插入就直接计算 上一个和当前点的终点之差 新点 直接计算差值*
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100010;
struct node{int l,r;
}N[maxn];
bool cmp(node a,node b){return a.l<b.l||(a.l==b.l&&a.r<b.r);
}
multiset<int>ms;
multiset<int>::iterator it;
int main()
{int t;scanf("%d",&t);while(t--){int n;scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d%d",&N[i].l,&N[i].r);sort(N+1,N+1+n,cmp);ll ans=0;for(int i=1;i<=n;i++){it = ms.upper_bound(N[i].l);if(it==ms.begin()){ans+=N[i].r-N[i].l;ms.insert(N[i].r);}else{it--;ans+=N[i].r-(*it);ms.insert(N[i].r);ms.erase(it);}}printf("%d %lld\n",ms.size(),ans);ms.clear();}return 0;
}