1. 程式人生 > 實用技巧 >牛客每日一題 NC17315揹包 (二分,優先佇列)

牛客每日一題 NC17315揹包 (二分,優先佇列)

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
≤ 1e9, v ≤ 1e9, bi ≤ 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