牛客每日一題 NC17315揹包 (二分,優先佇列)
阿新 • • 發佈:2020-07-30
https://ac.nowcoder.com/acm/problem/17315
題目描述
Applese有1個容量為v的揹包,有n個物品,每一個物品有一個價值ai,以及一個大小bi然後他對此提出了自己的疑問,如果我不要裝的物品裝的價值最大,只是一定需要裝m個物品,要使得求出來的物品價值的中位數最大
Applese覺得這個題依然太菜,於是他把這個問題丟給了你
當物品數量為偶數時,中位數即中間兩個物品的價值的平均值
輸入描述:
第一行三個數v, n, m,分別代表揹包容量,物品數量以及需要取出的物品數量接下來n行,每行兩個數ai,bi,分別代表物品價值以及大小 n ≤ 1e5, 1 ≤ m ≤ n, ai
輸出描述:
僅一行,代表最大的中位數
輸入
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(View Code"%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 */