D. Fast and Fat(2023 ICPC 山东省赛)(二分+贪心检查)

传送门

 题意:

        对于一个运动员 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<bits/stdc++.h>
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<x) que2.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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值