【贪心】【P4053】[JSOI2007] 建筑抢修

【贪心】【P4053】[JSOI2007] 建筑抢修

Description

n 个工作,第 i 个工作做完需要 ai 的时间,并且必须在 bi 时刻前完成。求最多能按时完成多少个工作

Limitations

1n1500001aibi2147483647

Solution

随机跳题跳到一个看上去很经典的贪心,以前只听说过从来没做到过题。

首先一个显然的结论是,对于完成工作的顺序序列 p,如果我将 p 里面的元素按照 bi 单调不降的顺序排序,得到的序列一定也是一个合法的能够完成序列内所有工作的序列。证明上可以考虑相邻两项,如果前面一项比后面一项的 b 大,那么交换相邻两项一定也可以完成任务。数学归纳得到按照 b 的不降序排序是合法的。例如,如果 {1, 5, 3} 的顺序可以完成 3 项任务,并且 b1b3b5,那么 {1, 3, 5} 一定也可以完成三项任务。

因此我们直接对任务序列按照 b 不降序排序,依次考虑是否选择每个任务。

考虑枚举到一个任务时,如果我们已经选择的任务用时加上该任务的用时不会超出该任务的时限,那么我们先贪心的将它选入任务序列。

考虑如果选入这个任务超时了,那么我们考虑将前面一个任务去掉,从而让这个任务被选入。显然所有任务的总用时越低越好,因从我们考虑去掉前面用时最长的任务,如果前面用时最长的任务比当前任务用时长,那么去掉前面的任务后当前任务一定能被选入,因为总用时减少但是时限增加了。并且我们在保证任务总数不变的情况下尽可能减少了总用时。因此这个贪心是正确的。当然如果前面最长的不如当前用时长,那就不再选入当前任务。

用堆去维护所有被选入的任务的时常即可。时间复杂度 O(nlogn)

Code

#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#define int ll
#endif

typedef long long ll;

namespace IPT {
  const int L = 1000000;
  char buf[L], *front=buf, *end=buf;
  char GetChar() {
    if (front == end) {
      end = buf + fread(front = buf, 1, L, stdin);
      if (front == end) return -1;
    }
    return *(front++);
  }
}

template <typename T>
inline void qr(T &x) {
  char ch = IPT::GetChar(), lst = ' ';
  while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
  while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
  if (lst == '-') x = -x;
}

namespace OPT {
  char buf[120];
}

template <typename T>
inline void qw(T x, const char aft, const bool pt) {
  if (x < 0) {x = -x, putchar('-');}
  int top=0;
  do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
  while (top) putchar(OPT::buf[top--]);
  if (pt) putchar(aft);
}

const int maxn = 150005;

int n, timeused, ans;
int A[maxn], B[maxn], MU[maxn];

struct Cmp {
  inline bool operator()(const int a, const int b) {
    return (A[a] == A[b]) ? (a < b) : (A[a] < A[b]);
  }
};

bool cmp(const int x, const int y);
std::priority_queue<int, std::vector<int>, Cmp> Q;

signed main() {
  freopen("1.in", "r", stdin);
  qr(n);
  for (int i = 1; i <= n; ++i) {
    qr(A[i]); qr(B[i]); MU[i] = i;
  }
  std::sort(MU + 1, MU + 1 + n, cmp);
  for (int p = 1, i = MU[p]; p <= n; i = MU[++p]) {
    if ((timeused + A[i]) > B[i]) {
      if (A[Q.top()] > A[i]) {
        timeused -= A[Q.top()];
        Q.pop();
        --ans;
      }
    }
    if ((timeused + A[i]) <= B[i]) {
      Q.push(i);
      timeused += A[i];
      ++ans;
    }
  }
  qw(ans, '\n', true);
  return 0;
}

inline bool cmp(const int x, const int y) {
  return (B[x] != B[y]) ? (B[x] < B[y]) : (x < y);
}
posted @   一扶苏一  阅读(242)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2018-10-30 【神仙题】【CF28D】 Don't fear, DravDe is kind
2018-10-30 【线段树】【CF19D】 Points
2018-10-30 【字符串】KMP字符串匹配
点击右上角即可分享
微信分享提示