[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;
        }

}
posted @ 2024-08-15 21:19  是一只小蒟蒻呀  阅读(13)  评论(0编辑  收藏  举报