P1752 点菜
under_the_time · · 题解
题意
有
n 个人,每个人每周至多选一道菜吃掉,求吃完m 道菜的最小时间或无解。这
m 道菜有美味值和价格两个参数,n 个人中p 个人只能吃美味值大于等于一定值的菜,q 个人只能吃价格小于等于一定值的菜,其余人没有限制。
解法
可以发现如果 check 的部分则贪心检查。
根据题意,一个可行的贪心方案是:总是让有限制的人群优先选择菜,且限制宽松的人总是吃掉条件更苛刻的(例如更贵的、更难吃的)菜,从而让限制较大的人产生更大的贡献。
这里我们选择优先考虑挑剔的
先将挑剔的人的美味值下限
做法原因:因为对原数组进行过排序,第
肯定有更简洁的方法,这种方法只是写起来清晰一些o.O
考虑完所有的菜后,如果还有挑剔的人没有被考虑,此时队列里的菜他们肯定都能吃,所以让他们去尽可能吃光队列里的菜。最后队列里剩下的菜交给穷人和普通人处理。
将贫穷的
考虑完所有贫穷的人后,队列里剩下的菜和被记录的菜由剩下的
实现
考虑挑剔的人时可以用两个指针,分别记录当前考虑到了哪道菜、哪个人,待处理队列使用优先队列(按照价格从大到小)。假设当前二分的答案为
设有
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 50005,maxm = 2e5 + 5;
ll P[maxn],Q[maxn]; int q,n,m,p,ans = -1; pair<ll,ll> M[maxm];
bool check(int week) {
int j = 1,tot = 0; priority_queue<int> pq;
for (int i = 1;i <= m;i ++) {
for (;j <= p && P[j] > M[i].first;j ++)
for (int k = 1;k <= week;k ++)
if (pq.empty()) break;
else pq.pop();
pq.push(M[i].second);
}
for (;j <= p;j ++)
for (int k = 1;k <= week;k ++)
if (pq.empty()) break;
else pq.pop();
for (int i = 1;i <= q;i ++) {
while (!pq.empty() && Q[i] < pq.top())
pq.pop(), tot ++;
for (int k = 1;k <= week;k ++)
if (pq.empty()) break;
else pq.pop();
}
return (n - p - q) * week >= tot + pq.size();
}
int main() {
scanf("%d%d%d%d",&n,&m,&p,&q);
for (int i = 1;i <= m;i ++)
scanf("%lld%lld",&M[i].first,&M[i].second);
for (int i = 1;i <= p;i ++) scanf("%lld",&P[i]);
for (int i = 1;i <= q;i ++) scanf("%lld",&Q[i]);
sort(M + 1,M + m + 1); reverse(M + 1,M + m + 1);
sort(P + 1,P + p + 1); reverse(P + 1,P + p + 1);
sort(Q + 1,Q + q + 1); reverse(Q + 1,Q + q + 1);
int l = 1,r = m,mid;
while (l <= r) {
mid = l + r >> 1;
if (check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
printf("%d",ans);
return 0;
}
- AC记录