[洛谷P2107] 小Z的AK计划
题目类型:贪心,堆
传送门:>Here<
题意:给出\(N\)个房间,每个房间距离起点的距离为\(x[i]\),每个房间可以选择进去和不进去,如果进去了那么要\(t[i]\)秒后才能出来。问在\(M\)秒内最多能进多少个房间
解题思路
第一眼是一个\(01\)背包,然而枚举当前房间和上一个房间,加上所用时间,复杂度\(O(n^3)\)……
考虑枚举终点,这样所有路上的时间之和就可以确定了。然后就是看在剩余的时间里最多能去几个房间,这个很简单——假设全去,如果超时,那么每次踢出耗时最多的那个房间。每次踢出最大的,用一个大根堆维护即可。复杂度\(O(nlogn)\)
反思
不要认为一道题是\(DP\)就死往\(DP\)里钻……其实可能根本不是\(DP\)而是贪心。贪心和\(DP\)在很多情况下同时适用于一个问题。
不能被题目迷惑……题目所说的距离和进入房间所需要的都是时间,好像混为一谈。实际上要是割裂开来看就异常简单了。
Code
/*By DennyQi 2018*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
#define int ll
const int MAXN = 100010;
const int INF = 1061109567;
inline int Max(const int a, const int b){ return (a > b) ? a : b; }
inline int Min(const int a, const int b){ return (a < b) ? a : b; }
inline int read(){
int x = 0; int w = 1; register char c = getchar();
for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
if(c == '-') w = -1, c = getchar();
for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
}
struct Room{
int x, t;
}a[MAXN];
inline bool operator < (const Room& a, const Room& b){
return a.t < b.t;
}
int N,M,ans,sum,cnt;
priority_queue <Room> q;
inline bool pos_cmp(const Room& a, const Room& b){
return a.x < b.x;
}
signed main(){
N = read(), M = read();
for(int i = 1; i <= N; ++i){
a[i].x = read();
a[i].t = read();
}
sort(a+1, a+N+1, pos_cmp);
for(int i = 1; i <= N; ++i){
q.push((Room){a[i].x, a[i].t});
sum += a[i].t + a[i].x - a[i-1].x;
while(sum > M && q.size()){
sum -= q.top().t;
q.pop();
--cnt;
}
++cnt;
ans = Max(ans, cnt);
}
printf("%d", ans);
return 0;
}