BZOJ3174. [TJOI2013]拯救小矮人(dp)

题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=3174

题解

其实此题并不需要那么多YY的部分。

我们考虑若干个小矮人逃出的顺序。若跳出的 \(k\) 个小矮人依次为 \(p_1, p_2, \cdots , p_k\),那么我们一定可以将他们排序,使得对于任意的 \(i < j\) 满足 \(a_i + b_i < a_j + b_j\) 且按照该顺序依然能保证 \(k\) 个小矮人全部逃出。

证明如下:

若对于任意 \(i, j\),有 \(a_i + b_i < a_j + b_j\),且 \(j\)\(i\) 先逃出,那么设 \(i\) 后面所有人的高度贡献为 \(s_1\)\(i\)\(j\) 之间的人的高度贡献为 \(s_2\)\(h\) 为洞深,可得:

\[a_i + a_j +b_j \geq h - s_1 - s_2 \tag{1} \]

\[a_i + b_i \geq h - s_1 \tag{2} \]

通过 \((2)\),我们可以推得:$$a_j + a_i + b_i \geq h - s_1 - s_2 \tag{3}$$

通过 \((2)\) 以及 \(a_i + b_i < a_j + b_j\),我们可以推得:$$a_j + b_j \geq h - s_1 \tag{4}$$

\((3), (4)\) 可以得出:\(i\)\(j\) 先逃出依然是合法的。

这样,我们就证明了排序的正确性,那么就可以先排序再 dp 了。一个比较显然的状态是,我们设 \(f_{i, j}\) 表示考虑完排序后的前 \(i\) 个人,且已经逃走的人的 \(\sum a_k\) 的值为 \(j\) 时,最多能逃走多少人。不过由于 \(\sum a_k\) 可能很大,这样定义状态并不可行,因此我们需要把状态的第二维与状态本身的意义交换一下:设 \(f_{i, j}\) 表示考虑完排序后的前 \(i\) 个人,且已经逃走了 \(j\) 个人,这 \(j\) 个人的 \(\sum a_k\) 的最小值。这样,我们就能在 \(O(n^2)\) 的时间内完成这个 dp 了。

代码

#include<bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long ll;
typedef long double ld;
typedef unsigned int uint;
typedef pair<int, int> pii;
typedef unsigned long long ull;

template<typename T> inline void read(T& x) {
  char c = getchar();
  bool f = false;
  for (x = 0; !isdigit(c); c = getchar()) {
    if (c == '-') {
      f = true;
    }
  }
  for (; isdigit(c); c = getchar()) {
    x = x * 10 + c - '0';
  }
  if (f) {
    x = -x;
  }
}

template<typename T, typename... U> inline void read(T& x, U&... y) {
  read(x), read(y...);
}

template<typename T> inline bool checkMax(T& a, const T& b) {
  return a < b ? a = b, true : false;
}

template<typename T> inline bool checkMin(T& a, const T& b) {
  return a > b ? a = b, true : false;
}

const int N = 2e3 + 10, inf = 0x3f3f3f3f;

struct State {
  int x, y;
  State () {}
  State (int x, int y): x(x), y(y) {}
  bool operator < (const State& a) const {
    return x + y < a.x + a.y;
  }
} s[N];

int n, f[N][N], h;

int main() {
  read(n);
  int sa = 0;
  for (register int i = 1; i <= n; ++i) {
    read(s[i].x, s[i].y), sa += s[i].x;
  }
  sort(s + 1, s + 1 + n);
  read(h);
  memset(f, 0x3f, sizeof f);
  f[0][0] = 0;
  for (register int i = 1; i <= n; ++i) {
    for (register int j = 0; j <= i; ++j) {
      f[i][j] = f[i - 1][j];
    }
    for (register int j = 1; j <= i; ++j) {
      if (sa - f[i - 1][j - 1] + s[i].y >= h) {
        checkMin(f[i][j], f[i - 1][j - 1] + s[i].x);
      }
    }
  }
  int ans = 0;
  for (register int i = 1; i <= n; ++i) {
    if (f[n][i] < inf) {
      ans = i;
    }
  }
  printf("%d\n", ans);
  return 0;
}
posted @ 2018-10-19 17:31  ImagineC  阅读(218)  评论(0编辑  收藏  举报