【GOJ 3038】逛餐館
阿新 • • 發佈:2020-09-15
解析
顯然我們只會往一個方向走,不會回頭(回頭一定浪費時間)。那我們將所有餐館序。
那我們不妨列舉我們能走到最遠的餐館,假設我們列舉我們走到第 \(i\)個餐館(排完序之後)。
那麼我們就是在 \(1\) ~ \(i\) 餐館中尋若干個餐館,使他們和不超過 \(m-x_i\),然後求最多的個數即可。那顯然就是在 \(1\) ~ \(i\) 餐館中按照 \(t_i\) 從小到大排序,然後不斷累加直至超過 \(m-x_i\) 的餐館個數。
這個經典題目用優先佇列來維護即可。
我們維護一個大根堆,這個堆裡面維護 \(1\) ~ \(i\) 所有餐館中,最小的若干個餐館,並且他們和 \(\le m-x_i\)
那我們從 \(1\) 到 \(n\) 列舉 \(i\) ,每次執行三個操作:
- 把 \(t_i\) 加進堆裡面。
- 若堆中元素之和大於 \(m-x_i\),則不斷彈出堆頂元素直到總和小於等於 \(m-x_i\)。
- 堆的大小就是我們能吃的餐館的個數,用這個更新答案。
時間複雜度為 \(O(n\ \log_2n)\)。
程式碼
#include<bits/stdc++.h> using namespace std; using LL=long long; const LL N=1e5+5; struct jvav { LL x,t; void read() { scanf("%lld %lld",&x,&t); } bool operator<(const jvav &bee) const { return x<bee.x; } } a[N]; priority_queue<LL> q; int main() { LL n,m; scanf("%lld %lld",&n,&m); for(LL i=1; i<=n; i++) { a[i].read(); } sort(a+1,a+n+1); LL sum=0,ans=0; for(LL i=1; i<=n; i++) { q.push(a[i].t),sum+=a[i].t; while(q.size()&&sum>m-a[i].x) { sum-=q.top(),q.pop(); } if(ans<q.size()) { ans=q.size(); } } printf("%lld",ans); return 0; }