Typesetting math: 100%

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

题目链接

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

题解

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

我们考虑若干个小矮人逃出的顺序。若跳出的 k 个小矮人依次为 p1,p2,,pk,那么我们一定可以将他们排序,使得对于任意的 i<j 满足 ai+bi<aj+bj 且按照该顺序依然能保证 k 个小矮人全部逃出。

证明如下:

若对于任意 i,j,有 ai+bi<aj+bj,且 ji 先逃出,那么设 i 后面所有人的高度贡献为 s1ij 之间的人的高度贡献为 s2h 为洞深,可得:

(1)ai+aj+bjhs1s2

(2)ai+bihs1

通过 (2),我们可以推得:(3)aj+ai+bihs1s2

通过 (2) 以及 ai+bi<aj+bj,我们可以推得:(4)aj+bjhs1

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

这样,我们就证明了排序的正确性,那么就可以先排序再 dp 了。一个比较显然的状态是,我们设 fi,j 表示考虑完排序后的前 i 个人,且已经逃走的人的 ak 的值为 j 时,最多能逃走多少人。不过由于 ak 可能很大,这样定义状态并不可行,因此我们需要把状态的第二维与状态本身的意义交换一下:设 fi,j 表示考虑完排序后的前 i 个人,且已经逃走了 j 个人,这 j 个人的 ak 的最小值。这样,我们就能在 O(n2) 的时间内完成这个 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 @   ImagineC  阅读(232)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示