题意:
对于一个运动员 i ,他的速度是 vi,体重是 wi。如果运动员 j 的体重 wj <= wi,那么运动员 i 可以用原本的速度 vi 背着运动员 j 奔跑。如果 wj > wi,那么速度就变成了 vi - ( wi - wj ) (如果是负数,那么运动员 i 不能背着 j 奔跑)。
给定一个T,代表有T组数据。每组数据第一行输入一个n,代表这个团队有n个人,后面n行,每行两个数vi,wi。运动员可以被背着跑,也不能不背着人跑。求这个团队奔跑起来后速度最慢的那个人 的速度最大值。
思路:
我们可以发现,当vi+wi > vj+wj 时,i 一定可以背着 j 跑。
考虑取速度最大值和最小值,然后不断二分找答案。
在二分答案时,对于答案 x ,选出大于等于速度 x 的人当中 vi+wi 最高的几个人,分别去背着速度小于 x 的人当中 wi 最大的人奔跑。
代码:
- #include
- using namespace std;
- const int N = 1e5+5;
- #define int long long
- typedef pair<int, int>PII;
- const int Max = 0x3f3f3f3f3f3f3f3f;
- const int Min = -0x3f3f3f3f3f3f3f3f;
- int n, m, k;
- int T;
- PII q1[N], q2[N];
- queue<int>que, que2;
- bool cmp(PII aa, PII bb)
- {
- return aa.first+aa.second > bb.first+bb.second;
- }
- bool cmp2(PII aa, PII bb)
- {
- return aa.second > bb.second;
- }
- int check(int x)
- {
- while(que.size()) que.pop(); //多组输入,每次使用队列前都要清空
- while(que2.size()) que2.pop();
- //如果速度大于等于当前 x,那么将vi+wi从大到小存入队列(已经排完序了)
- for(int i = 0; i < n; i ++)
- {
- if(q1[i].first>=x) que.push(q1[i].first + q1[i].second - x);
- }
- //如果速度小于当前的 x, 那么将wi从大到小存入队列
- for(int i = 0; i < n; i ++)
- {
- if(q2[i].first
push(q2[i].second); - }
- //如果可以背人的人数小于需要被人背着的人数,直接false
- if(que.size() < que2.size()) return false;
- //一一对应,队列中vi+wi最大的去背着队列中wi最大的,队列中vi+wi第二大的去背着队列wi第二大的...
- while(que2.size())
- {
- if(que.front() < que2.front()) return false;
- que.pop(), que2.pop();
- }
- return true;
- }
- signed main()
- {
- ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
- cin >> T;
- while(T --)
- {
- int head = Min, tail = Max;
- cin >> n;
- for(int i = 0; i < n; i ++)
- {
- int x, y;
- cin >> x >> y;
- q1[i].first = q2[i].first = x;
- q1[i].second = q2[i].second = y;
- head = max(x, head); //求出速度的最大值和最小值
- tail = min(x, tail);
- }
- sort(q1, q1+n, cmp); //pair 排序(根据vi+wi从大到小)
- sort(q2, q2+n, cmp2); // 根据wi从大到小排序
- // 二分答案
- while(tail < head)
- {
- int mid = head + tail + 1 >> 1;
- if(check(mid)) tail = mid;
- else head = mid - 1;
- }
- cout << head << "\n";
- }
- return 0;
- }