牛客每日一题 NC17315背包 (二分,优先队列)

https://ac.nowcoder.com/acm/problem/17315

题目描述

Applese有1个容量为v的背包,有n个物品,每一个物品有一个价值a i,以及一个大小b i
然后他对此提出了自己的疑问,如果我不要装的物品装的价值最大,只是一定需要装m个物品,要使得求出来的物品价值的中位数最大
Applese觉得这个题依然太菜,于是他把这个问题丢给了你
当物品数量为偶数时,中位数即中间两个物品的价值的平均值

输入描述:

第一行三个数v, n, m,分别代表背包容量,物品数量以及需要取出的物品数量
接下来n行,每行两个数a i,b i,分别代表物品价值以及大小
n ≤ 1e5, 1 ≤ m ≤ n, a i ≤ 1e9, v ≤ 1e9, b i ≤ v

输出描述:

仅一行,代表最大的中位数

输入

20 5 3
3 5
5 6
8 7
10 6
15 10

输出

8

题意:

n个物品取m个,容量<=v,求取的m个物品的价值最大中位数是多少?

中位数就是最中间的数,m为奇数即为第m/2+1个,m为偶数即为第(m/2)个和第(m/2+1)个的平均值

题解:

 我们可以用优先队列和2个sum数组 分为维护左边m/2个的重量和的最小值 以及右边m/2个的重量和的最小值

如果m为奇数,那我们直接枚举答案就可以

如果m为偶数,我们直接维护左边m/2的重量最小值不是不行的,因为中位数是中间两个数字的平均值,所以我们用优先队列和2个sum数组 分为维护左边m/2-1的重量最小值 以及右边m/2的重量最小值,我们枚举第一个中间的数,第二个中间的数是右边m/2个的最左边的数,因为序列是升序的,我们可以用二分查找最大的右边m/2的最左边的数。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
ll sum[maxn],sum2[maxn];
vector < pair<ll ,ll > > a;
int main(){
    int v,n,m,k;
    scanf("%d%d%d",&v,&n,&m);
    ll x,y;
    a.push_back ( make_pair(0,0));
    for(int i=0;i<n;i++){
        scanf("%lld%lld",&x,&y);
        a.push_back ( make_pair(x,y));
    }
    sort(a.begin(),a.end());
    priority_queue<ll> p;
    for(int i=1;i<=n;i++){
        p.push(a[i].second);
        sum[i]=sum[i-1]+a[i].second;
        if(p.size()>m/2-1+m%2){
            sum[i]-=p.top();        
            p.pop();
        }
    }
    while(!p.empty()) p.pop();
    for(int i=n;i>=1;i--){
        p.push(a[i].second);
        sum2[i]=sum2[i+1]+a[i].second;
        if(p.size()>m/2 ){
            sum2[i]-=p.top();        
            p.pop();
        }
    }
//    for(int i=0;i<=n;i++){
//        cout<<a[i].first<<" "<<a[i].second<<endl;
//    }
//    for(int i=0;i<=n;i++){
//        cout<<sum[i]<<" ";
//    }
//    cout<<endl;
//    for(int i=0;i<=n;i++){
//        cout<<sum2[i]<<" ";
//    }
    ll ans=0;
    if(m&1){
        for(int i=m/2;i<=n-m/2;i++){
            if(sum[i-1]+sum2[i+1]+a[i].second<=v) ans=a[i].first;
        }
        cout<<ans<<endl;
    }else{
        for(int i=m/2;i<=n-m/2;i++){
            int l=i+1,r=n-m/2+1;
            while(l<=r){
                int mid=(l+r)>>1;
                if(sum[i-1]+sum2[mid]+a[i].second<=v) l=mid+1;
                else r=mid-1;
            }
            if(r>i) ans=max(ans,1ll+a[i].first+a[r].first-1);
        }
        cout<<ans/2<<endl;
    }
    return 0;
}
/*
20 5 4
3 5
5 4
8 5
10 4
15 6
*/
View Code

猜你喜欢

转载自www.cnblogs.com/lyj1/p/13403480.html