• 作者:老汪软件技巧
  • 发表时间:2023-12-28 09:00
  • 浏览量:

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

题意:

对于一个运动员 i ,他的速度是 vi,体重是 wi。如果运动员 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 pairPII;
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
int n, m, k;
int T;
PII q1[N], q2[N];
queueque, 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> 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;
}