牛客网 Wannafly 挑战赛 20 B背包

题目链接:https://www.nowcoder.com/acm/contest/133/B

链接:https://www.nowcoder.com/acm/contest/133/B
来源:牛客网
注意:本文章的第一份代码是借鉴的牛客网的https://www.nowcoder.com/acm/contest/profile/6799934#report  的代码

双重遍历是自己改的,但是时间消耗太大,二分法是同学的想法启发我减少时间开销,我是小白勿喷,嘤嘤嘤谢谢

题目描述

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

输入描述:

第一行三个数v, n, m,分别代表背包容量,物品数量以及需要取出的物品数量

接下来n行,每行两个数ai,bi,分别代表物品价值以及大小

n ≤ 1e5, 1 ≤ m ≤ n, ai ≤ 1e9, v ≤ 1e9, bi ≤ v

输出描述:

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

示例1

输入

复制

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

输出

复制

8

分析:结构体存储物品,按照物品价值从小到大排序:

分两种情况 :1:m是奇数,将排好序的全部数据分成3段,中间是要求的是最大中位数,前面算出取前面几个数的所占有的最小容量,用数组保存,利用优先队列的性质,后面那段同理,计算出取后面几个数所占有的最小容量。因为要使中位数最大,就要尽量给中位数多留点空间,其余的物品价值多大不用管,只管枚举中间的数,看是否满足情况。然后每一次递推求出最大的中位数

2:m是偶数,同样分成3段,不过前面那段比后面那段少一个数据。比如m是6 ,一个数组维护前i个数据放2个物品所占有的最小空间,另一个数组维护后i个数据所占有的最小空间,所以就枚举中间的数,递推出最大值、

代码如下:

#include<bits/stdc++.h>
#define ll long long
#define M 5000005
using namespace std;
struct node{
    int a,b;
    bool operator < (const node &_)const{
        return a<_.a;
    }
}A[M];
int V,n,m;
priority_queue<int>q;
ll Mn[2][M];
int main(){
    scanf("%d%d%d",&V,&n,&m);
    for(int i=1;i<=n;i++)scanf("%d%d",&A[i].a,&A[i].b);
    sort(A+1,A+1+n);
    if(m%2){
        int cnt=0;ll w=0;
        for(int i=1;i<=n;i++){
            if(cnt<m/2){
                q.push(A[i].b);
                cnt++;
                w+=A[i].b;
            }else{
                int a=q.top();
                if(a>A[i].b){
                    q.pop();
                    q.push(A[i].b);
                    w=w-a+A[i].b;
                }
            }
            Mn[0][i]=w;
        }
        while(!q.empty())q.pop();w=0;cnt=0;
        for(int i=n;i>=1;i--){
            if(cnt<m/2){
                q.push(A[i].b);
                cnt++;
                w+=A[i].b;
            }else{
                int a=q.top();
                if(a>A[i].b){
                    q.pop();
                    q.push(A[i].b);
                    w=w-a+A[i].b;
                }
            }
            Mn[1][i]=w;
        }
        ll ans=0;
        for(int i=m/2+1;i<=n-m/2;i++){
            ll v=Mn[0][i-1]+Mn[1][i+1]+A[i].b;
            if(v<=V)ans=max(ans,1ll*A[i].a);
        }
        printf("%lld\n",ans);
    }else{
        int cnt=0;ll w=0;
        for(int i=1;i<=n;i++){
            if(cnt<m/2-1){
                q.push(A[i].b);
                cnt++;
                w+=A[i].b;
            }else{
                if(q.empty())continue;
                int a=q.top();
                if(a>A[i].b){
                    q.pop();
                    q.push(A[i].b);
                    w=w-a+A[i].b;
                }
            }
            Mn[0][i]=w;
        }
        while(!q.empty())q.pop();w=0;cnt=0;
        for(int i=n;i>=1;i--){
            if(cnt<m/2){
                q.push(A[i].b);
                cnt++;
                w+=A[i].b;
            }else{
                if(q.empty())continue;
                int a=q.top();
                if(a>A[i].b){
                    q.pop();
                    q.push(A[i].b);
                    w=w-a+A[i].b;
                }
            }
            Mn[1][i]=w;
        }
        ll ans=0;
        for(int i=m/2;i<=n-m/2;i++){
            ll v=Mn[0][i-1]+Mn[1][i+1]+A[i].b;
            if(v<=V)ans=max(ans,1ll*(A[i].a+A[i+1].a)/2);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

疑点:这里就有点奇怪了,因为中间的那个中位数在排序上不一定是连续的,在枚举中间那个数时,另一个数应该是越靠后就平均值越大,但是就只考虑连续的情况就能过

并且试过如下的代码:在偶数时是用平均分,就是中间两个数采用枚举,但是两边预处理的个数一样,这样考虑到了两个中位数不连续的情况,但是超时了,所以就想办法解决时间复杂度的问题

#include<bits/stdc++.h>
#define ll long long
#define M 5000005
using namespace std;
struct node{
    int a,b;
    bool operator < (const node &_)const{
        return a<_.a;
    }
}A[M];
int V,n,m;
priority_queue<int>q;
ll Mn[2][M];
int main(){
    scanf("%d%d%d",&V,&n,&m);
    for(int i=1;i<=n;i++)scanf("%d%d",&A[i].a,&A[i].b);
    sort(A+1,A+1+n);
    if(m%2){
        int cnt=0;ll w=0;
        for(int i=1;i<=n;i++){
            if(cnt<m/2){
                q.push(A[i].b);
                cnt++;
                w+=A[i].b;
            }else{
                int a=q.top();
                if(a>A[i].b){
                    q.pop();
                    q.push(A[i].b);
                    w=w-a+A[i].b;
                }
            }
            Mn[0][i]=w;
        }
        while(!q.empty())q.pop();w=0;cnt=0;
        for(int i=n;i>=1;i--){
            if(cnt<m/2){
                q.push(A[i].b);
                cnt++;
                w+=A[i].b;
            }else{
                int a=q.top();
                if(a>A[i].b){
                    q.pop();
                    q.push(A[i].b);
                    w=w-a+A[i].b;
                }
            }
            Mn[1][i]=w;
        }
        ll ans=0;
        for(int i=m/2+1;i<=n-m/2;i++){
            ll v=Mn[0][i-1]+Mn[1][i+1]+A[i].b;
            if(v<=V)ans=max(ans,1ll*A[i].a);
        }
        printf("%lld\n",ans);
    }else{
        int cnt=0;ll w=0;
        for(int i=1;i<=n;i++){
            if(cnt<m/2-1){
                q.push(A[i].b);
                cnt++;
                w+=A[i].b;
            }else{
                if(q.empty())continue;
                int a=q.top();
                if(a>A[i].b){
                    q.pop();
                    q.push(A[i].b);
                    w=w-a+A[i].b;
                }
            }
            Mn[0][i]=w;
        }
        while(!q.empty())q.pop();w=0;cnt=0;
        for(int i=n;i>=1;i--){
            if(cnt<m/2-1){
                q.push(A[i].b);
                cnt++;
                w+=A[i].b;
            }else{
                if(q.empty())continue;
                int a=q.top();
                if(a>A[i].b){
                    q.pop();
                    q.push(A[i].b);
                    w=w-a+A[i].b;
                }
            }
            Mn[1][i]=w;
        }
        ll ans=0;
        for(int i=m/2;i<=n-m/2;i++){
        	for(int j=i+1;j<=n-m/2+1;j++){
        		ll v=Mn[0][i-1]+Mn[1][j+1]+A[i].b+A[j].b;
        		if(v<=V)ans=max(ans,1ll*(A[i].a+A[j].a)/2);
			}
            
            
        }
        printf("%lld\n",ans);
    }
    return 0;
}

于是就通过二分法来减少时间开销,下面为二分法代码:

#include<bits/stdc++.h>
#define ll long long
#define M 5000005
using namespace std;
struct node{
    int a,b;
    bool operator < (const node &_)const{
        return a<_.a;
    }
}A[M];
int V,n,m;
priority_queue<int>q;
ll Mn[2][M];
int main(){
    scanf("%d%d%d",&V,&n,&m);
    for(int i=1;i<=n;i++)scanf("%d%d",&A[i].a,&A[i].b);
    sort(A+1,A+1+n);
    if(m%2){
        int cnt=0;ll w=0;
        for(int i=1;i<=n;i++){
            if(cnt<m/2){
                q.push(A[i].b);
                cnt++;
                w+=A[i].b;
            }else{
                int a=q.top();
                if(a>A[i].b){
                    q.pop();
                    q.push(A[i].b);
                    w=w-a+A[i].b;
                }
            }
            Mn[0][i]=w;
        }
        while(!q.empty())q.pop();w=0;cnt=0;
        for(int i=n;i>=1;i--){
            if(cnt<m/2){
                q.push(A[i].b);
                cnt++;
                w+=A[i].b;
            }else{
                int a=q.top();
                if(a>A[i].b){
                    q.pop();
                    q.push(A[i].b);
                    w=w-a+A[i].b;
                }
            }
            Mn[1][i]=w;
        }
        ll ans=0;
        for(int i=m/2+1;i<=n-m/2;i++){
            ll v=Mn[0][i-1]+Mn[1][i+1]+A[i].b;
            if(v<=V)ans=max(ans,1ll*A[i].a);
        }
        printf("%lld\n",ans);
    }else{
        int cnt=0;ll w=0;
        for(int i=1;i<=n;i++){
            if(cnt<m/2-1){
                q.push(A[i].b);
                cnt++;
                w+=A[i].b;
            }else{
                if(q.empty())continue;
                int a=q.top();
                if(a>A[i].b){
                    q.pop();
                    q.push(A[i].b);
                    w=w-a+A[i].b;
                }
            }
            Mn[0][i]=w;
        }
        while(!q.empty())q.pop();w=0;cnt=0;
        for(int i=n;i>=1;i--){
            if(cnt<m/2){
                q.push(A[i].b);
                cnt++;
                w+=A[i].b;
            }else{
                if(q.empty())continue;
                int a=q.top();
                if(a>A[i].b){
                    q.pop();
                    q.push(A[i].b);
                    w=w-a+A[i].b;
                }
            }
            Mn[1][i]=w;
        }
        ll ans=0;
        for(int i=n-(m-1)/2-1;i>(m-1)/2;i--){
        	int l=i+1;
        	int r=n-(m-1)/2;
        	int key=-1;
        	while(l<=r){
        		int mid=(l+r)>>1;
        		if(Mn[1][mid]+Mn[0][i-1]+A[i].b<=V) l=mid+1,key=mid;
        		else r=mid-1;
			}
			if(key==-1) continue;
			ans=max(ans,1LL*(A[key].a+A[i].a)/2);
		}
        printf("%lld\n",ans);
    }
    return 0;
}

谢谢观看,有好的方法欢迎指教。

猜你喜欢

转载自blog.csdn.net/qq_37774171/article/details/81143772