[lnsyoj3174/luoguP4823/TJOI2013]拯救小矮人
题意
给定序列 \(a,b\) 和常数 \(h\),若序列中存在值 \(k\) 满足 \(b_k+\sum_{i=1}^{\operatorname{len}(a)} a_i \ge h\),则可将 \(a_k,b_k\) 删除,求从 \(a\) 中删除的数的数量最大为多少。
sol
由于 \(b\) 越小的数越靠后越难被删除,同时,\(a\) 越大的数越可以帮助其他数字被删除,因此我们希望先被删除的数的 \(a\),和 \(b\) 都更小,因此我们按照 \(a+b\) 来进行排序,然后进行一次 DP,计算前 \(i\) 个值中,被删掉 \(j\) 个值剩下的所有值的 \(\sum a_i\) 为多少。此时,若该值 DP 值非负,说明这是一个合法状态,因此倒序枚举,若 DP 值非负,则直接输出即可。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 2005;
PII a[N];
int n, h;
int f[N][N];
bool cmp(PII a, PII b){
if (a.x + a.y != b.x + b.y) return a.x + a.y < b.x + b.y;
return a.x < b.x;
}
int main(){
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d%d", &a[i].x, &a[i].y);
scanf("%d", &h);
sort(a + 1, a + n + 1, cmp);
memset(f, -0x3f, sizeof f);
for (int i = 0; i <= n; i ++ ) {
f[i][0] = 0;
for (int j = 1; j <= n; j ++ )
f[i][0] += a[j].x;
}
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ ) {
f[i][j] = f[i - 1][j];
if (f[i - 1][j - 1] + a[i].y >= h) f[i][j] = max(f[i][j], f[i - 1][j - 1] - a[i].x);
}
// for (int i = 0; i <= n; i ++ ) printf("%d\n", f[n][i]);
for (int i = n; i >= 0; i -- )
if (f[n][i] >= 0) {
printf("%d\n", i);
return 0;
}
}