L - Neo-Robin Hood Gym - 103102L

题目连接
题意:题中给出一个n和两个数组,第一个数组表示抢劫此位置可以得到的金额,第二个数组表示救济此位置应该给予的金额,要求最终抢劫的人数不得大于救济的人数。问最多可以抢劫的人数。
注:不要求最终获得的金额最大,只要求抢劫的人数最大。
题解:我们初步看到这个认定这是一个贪心的题,但是排序的时候不知道按照什么排序。本来想的是按照他需要救济的从小到大排序。但是会发现如果救济他花费很小,但是抢劫他获得也很多。这样就会冲突了,我不知道是要抢劫他还是救济他。如果我按照抢劫数排序依然会遇到这个问题。现在我们发现只单独排序一个是不能解决问题的,所以我们考虑对于两个数按照一定的方法进行排序。假设我们抉择两个(A,B),判断是抢劫他们还是救济他们,那么我们分别抢劫或是救济他们的时候剩余的钱数分别是A1-B2,B1-A2,我们假设一方占优势,A1-B2<B1-A2 即改变顺序后A1+A2<B1+B2,我们看到了排序的条件,按照A1+A2排序,排序后就可以从前往后求解了。
整体的方法是二分答案,二分可以抢劫的人数(最多是n/2),具体的就是在check中维护一个前x个小的数的和和后x大的数的和,因为这里是A1+A2<B1+B2表示的是抢B1A1更优,所以往前面维护的是买的用的最少的,后面的维护的是抢的最多的。这样然后判断能否在后面找到前面的值。
下面是AC代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
#define int long long
const int N=1e5+10;
int pre[N],nxt[N];
int n;
struct node
{
    int x,y;
    bool operator <(node &w)
    {
        if(x+y!=w.x+w.y) return x+y<w.x+w.y;
        else return y<w.y;
    }
}a[N];
bool check(int x)
{
    priority_queue<int,vector<int>,greater<int> > Min;//最小的
    priority_queue<int> Max;//最大的
    int sum=0;
    for(int i=1;i<=n;i++)//维护花费最小x个数花费之和,这里是用Max来维护的
    {
        if(Max.size()<x) Max.push(a[i].y),sum+=a[i].y;
        else
        {
            if(a[i].y<Max.top())
            {
                sum-=Max.top();
                sum+=a[i].y;
                Max.pop();
                Max.push(a[i].y);
            }
        }
        pre[i]=sum;
    }
    sum=0;
    for(int i=n;i>=1;i--)//维护后缀的抢劫最大的x个数花费之和,这里是用Min来维护的
    {
        if(Min.size()<x) Min.push(a[i].x),sum+=a[i].x;
        else
        {
            if(a[i].x>Min.top())
            {
                sum-=Min.top();
                sum+=a[i].x;
                Min.pop();
                Min.push(a[i].x);
            }
        }
        nxt[i]=sum;
    }
    for(int i=x;i<=n-x;i++)
    {
        if(nxt[i+1]>=pre[i]) return true;
    }
    return false;
}
signed main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i].x;
    for(int i=1;i<=n;i++) cin>>a[i].y;
    sort(a+1,a+1+n);
    int l=0,r=n/2;
    while(l<r)
    {
        int mid=l+r+1>>1;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    cout<<l<<endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值