二分答案 2023山东省赛 Fast and Fat

[P9559 SDCPC2023] Fast and Fat - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

思路:最小值最大,二分答案。

发现对于 w i ≥ w j w_i \ge w_j wiwj时,第 i i i个人速度不变,还是 v i v_i vi,但是第 j j j个人速度会变为 v j − ( w i − w j ) v_j - (w_i - w_j) vj(wiwj)。直接二分答案的话,假设此时为 m i d mid mid,那么速度小于 m i d mid mid的人需要人来带,且速度要至少提到 m i d mid mid才符合要求。

且对于初始速度已经大于 m i d mid mid的来说,可能要带速度小于 m i d mid mid的人,而且大于 m i d mid mid的人要带的人的体重差也要满足一定条件,即要符合 v j − ( w i − w j ) ≥ m i d v_j - (w_i - w_j) \ge mid vj(wiwj)mid,最差情况是 v j − ( w i − w j ) = m i d v_j - (w_i - w_j) = mid vj(wiwj)=mid,进而得到所承受的最大重量为 w i = v j + w j − m i d w_i = v_j + w_j - mid wi=vj+wjmid。此时发现在最大重量中, v j + w j v_j + w_j vj+wj是不变的, v j + w j − m i d v_j + w_j - mid vj+wjmid是单调的。

对于需要两两组合的情况,显示是可能出现经过特定构造可以满足,但是随机分配是不满足的情况,此时还要贪心的进行配对。我们可以复制一份每个人的信息 v , w v,w v,w。对于其中一个按照 v i + w i v_i + w_i vi+wi进行排序,对于另一个按照 w i w_i wi进行排序,这样就贪心的保证:速度大于 m i d mid mid的人可以分配的最大体重的值跟速度小于 m i d mid mid的人的最大体重值相配对,次大依次类推,避免出现浪费 v i + w i v_i + w_i vi+wi w j w_j wj的情况,且如果这样都不行,那么一定不存在可行方案。

代码如下:

void solve() {
    int n; cin>>n;
    vector<array<int,2>> a(n); // v w
    for(auto &t: a) cin>>t[0]>>t[1];
    auto b(a);
    // 按 v + w 进行排序
    sort(a.begin(), a.end(), [](auto pre, auto suf) {
        return pre[0] + pre[1] > suf[0] + suf[1];
    });
    // 按 w 进行排序
    sort(b.begin(), b.end(), [](auto pre, auto suf) {
        return pre[1] > suf[1];
    });
    
    // pre为速度大于mid的,suf是速度小于mid
    // 得到pre可以带的最大体重,suf的体重,进行配对
    auto check = [&](int mid) -> bool {
        vector<int> pre, suf;
        for(int i = 0; i < n; ++i) {
            if(a[i][0] >= mid) pre.push_back(a[i][0] + a[i][1] - mid);
            if(b[i][0] < mid) suf.push_back(b[i][1]);
        }
        // 如果小于mid的人的数量大于速度大于mid的,那么一定不可以
        if(suf.size() > pre.size()) return false;
        int m = suf.size();
        for(int i = 0; i < m; ++i) {
            // 如果速度大于mid的人能带的最大体重小于速度小于mid的体重,那么就不行
            if(suf[i] > pre[i]) return false;
        }
        return true;
    };
    // 最小值最大
    int l = 0, r = 1e9;
    while(l < r) {
        int mid = l + r + 1 >> 1;
        if(check(mid)) l = mid;
        else r = mid - 1;
    }
    cout<<l<<'\n';
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

golemon.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值