抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

题目传送门

题意

给定 \(n\) 个二元组 \((a_i,b_i)\)。一个二元组 \((a,b)\) 可以变为 \((a+t,b-t)\ (0\le t\le b)\)。你现在可以选择一些二元组并将它们分别进行(即互相独立)一次变换,使得变换后你选择的所有二元组 \((a_j,b_j)\)\(\sum a_j\ge A,\sum b_j\ge B\)。求最少需要选择并变换的二元组数量。

\(1\le n\le 50,1\le A,B,a_i,b_i\le 10^4\)

题解

转化题意:给定 \(n\) 个二元组 \((b_i,a_i+b_i)\),选择最少的二元组使得所有选择的二元组 \(\sum b_j\ge B,\sum (a_j+b_j)\ge A+B\)

于是就可以 DP 了。\(dp_{i,j}\) 表示选择 \(i\) 个二元组,\(\sum b_i=j\) 时,\(\sum (a_i+b_i)\) 的最大值。

考虑把 \(n\) 个二元组依次加入,DP 方程很容易写出。

发现直接写,第二维有百万级别,怎么办?\(j>B\) 的状态都合并到 \(j=B\) 处即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <bits/stdc++.h>
int n, A, B, a[55], b[55], dp[55][10005];
void cmx(int &x, int y){ x = std :: max(x, y); }
int main(){
scanf("%d%d%d", &n, &B, &A), B += A; // 与上面的 A,B 有区别,注意区分
for (register int i = 1; i <= n; ++i) scanf("%d%d", b + i, a + i), b[i] += a[i]; // 与上面的 ai,bi 有区别,注意区分
memset(dp, -1, sizeof dp), dp[0][0] = 0;
for (register int i = 1; i <= n; ++i) // 依次加入
for (register int j = i - 1; ~j; --j) // 倒着做,与 01 背包同理
for (register int k = 0; k <= A; ++k)
if (~dp[j][k]) cmx(dp[j + 1][k + a[i] > A ? A : k + a[i]], dp[j][k] + b[i]);
for (register int i = 1; i <= n; ++i) if (dp[i][A] >= B) return printf("%d\n", i), 0;
return printf("-1\n"), 0;
}

评论