题目描述
Applese有1个容量为v的背包,有n个物品,每一个物品有一个价值ai,以及一个大小bi
然后他对此提出了自己的疑问,如果我不要装的物品装的价值最大,只是一定需要装m个物品,要使得求出来的物品价值的中位数最大
Applese觉得这个题依然太菜,于是他把这个问题丢给了你
当物品数量为偶数时,中位数即中间两个物品的价值的平均值
解题思路:
- 中位数取决于中间两个数的大小
- 先对物品根据价值进行排序
- m为奇数时直接枚举中间的那个数,左边选m/2-1个体积最小的,右边选m/2-1个体积最小的
- m为偶数时,考虑枚举右半部分的最小价值,这个最小价值对应了一个最小体积,然后多出来了部分体积,在这部分体积里选择价值最大的,需要用数据结构来维护。
- 对于偶数,维护两个数组L[],R[]。R数组表示从i到n选Rsz个体积最小的物品。L数组表示从0到i选Lsz个物品,且价值最大的是a[i],对应的最小体积
#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define me0(a) memset(a,0,sizeof(a))
#define me1(a) memset(a,-1,sizeof(a))
#define op freopen("in.txt", "r", stdin)
#define pii pair<int,int>
#define mp(x,y) make_pair(x,y)
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
void read(int& val) { int x = 0; int bz = 1; char c; for (c = getchar(); (c<'0' || c>'9') && c != '-'; c = getchar()); if (c == '-') { bz = -1; c = getchar(); }for (; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - 48; val = x * bz; }
const int mod=1e9+7;
const int maxn = 1e6+10;
int n,m,v;
struct node{
int a,b;//价值,体积
bool operator<(const node t)const {
return a<t.a;
}
}p[maxn];
LL R[maxn], L[maxn];
void init() {
int Rsz = (m + 1) >> 1, Lsz = m - Rsz;
priority_queue<int> q;
q.push(1e9 + 1);
LL nwsum = 1e9 + 1;
//R数组表示从i到n选Rsz个体积最小的物品,L数组表示从0到i选Lsz个物品,且体积最大的是i,的最小体积
fod(i, n, 1) {
q.push(p[i].b);
nwsum += p[i].b;
if (q.size() > Rsz) {
nwsum -= q.top();q.pop();
R[i] = nwsum;
}
else {
R[i] = v + 1;
}
}
while (!q.empty()) q.pop();
q.push(1e9 + 1);
nwsum = 1e9 + 1;
fo(i, 1, n) {
if (q.size() == Lsz) {
L[i] = nwsum - q.top() + p[i].b;
q.push(p[i].b);
nwsum += p[i].b-q.top();
q.pop();
} else {
q.push(p[i].b);
nwsum += p[i].b;
L[i] = v + 1;
}
}
}
//将体积离散化
LL vb[maxn];
int tot=0;
int mx[maxn];
void up(int root){
mx[root] = max(mx[root<<1],mx[root<<1|1]);
}
int qu(int root,int l,int r,int R){
if(r<=R) return mx[root];
if(l>R) return 0;
int mid=l+r>>1;
return max(qu(root<<1,l,mid,R),qu(root<<1|1,mid+1,r,R));
}
void upd(int root,int l,int r,int pos,int v){
if(l==r) {
mx[root]=max(mx[root],v);return;
}
int mid=l+r>>1;
if(pos<=mid) upd(root<<1,l,mid,pos,v);
else upd(root<<1|1,mid+1,r,pos,v);
up(root);
}
void solve2() {
//选偶数个的策略是,枚举右边界,在剩余的体积内选择价值最大的
fo(i, 1, n) {
if (L[i] <= v) vb[++tot] = L[i];
if (R[i] <= v) vb[++tot] = R[i];
}
sort(vb + 1, vb + 1 + tot);
tot = unique(vb + 1, vb + 1 + tot) - vb - 1;
int ans = 0;
fo(i, 1, n) {
if (R[i] <= v) {
int mxv = lower_bound(vb + 1, vb + 1 + tot, v - R[i] + 1) - vb - 1;
if (mxv >= 1) {
int lans = qu(1, 1, tot, mxv);//在剩余体积里可以选的最大价值
ans =max(ans, lans + p[i].a);
}
}
if (L[i] <= v) {
upd(1, 1, tot, lower_bound(vb + 1, vb + 1 + tot, L[i]) - vb, p[i].a);
}
}
printf("%d\n", ans / 2);
}
void solve1(){
//选奇数个的策略是,枚举中间点,左边选体积最小的m/2-1个即可
priority_queue<int>q;
LL nw= 0;
int ans=0;
fo(i,1,n){
if(nw+R[i]<=v) ans=max(ans,p[i].a);
q.push(p[i].b);nw+=p[i].b;
if(q.size()>m/2) nw-=q.top(),q.pop();
}
printf("%d\n",ans);
}
int main(){
read(v);read(n);read(m);
fo(i,1,n) read(p[i].a),read(p[i].b);
sort(p+1,p+1+n);//对价值进行排序
init();
if(m==1){
int ans = 0;
fo(i,1,n){
if(p[i].b<=v) ans=max(ans,p[i].a);
}
printf("%d\n",ans);
}
else if(m&1) solve1();
else solve2();
return 0;
}
京公网安备 11010502036488号