题意:
给定n个瓶子,初始第 \(i\) 个瓶子装有水 \(a_i\),容积为 \(b_i\)。要把所有水装进尽量少的瓶子里,其次使要转移的水量最小。输出要用的瓶子数 \(K\) 和要转移的水量 \(ans\)
\(1\le n \le 100, 1\le a_i\le b_i\le 100\)
思路:
所有瓶子中的水量总和为 \(sumA\) 。按容积对所有瓶子从大到小排序,第 \(1\sim K\) 个瓶子的容积和 \(sumb\) 恰大于等于 \(sumA\) ,输出 \(K\)。
在 \(n\) 个瓶子中选 \(K\) 个。要使要转移的水量最小,则要使选出的 \(K\) 个瓶子中初始的水的总和最大。
\(dp[i][j][k]\) 表示在前 \(i\) 个瓶子中选 \(k\) 个,这 \(k\) 个瓶子的容积之和为 \(j\),这 \(k\) 个瓶子的最大初始水总和。
转移方程:\(dp[i][j][k]=\max\limits_{1\le i\le n} \{ dp[i-1][j][k],dp[i-1][j-b_i][k-1] + a_i \}\)
第一维可省略,\(j\) 从 \(sumb\) 枚举到 \(b_i\) 。
\(ans = \max \limits _{sumA\le j\le sumb} dp[j][K]\) ,初始化 \(dp[0][0]=0\),其他为负无穷
const int N = 110;
struct node {int a, b; } c[N];
bool cmp(node x, node y) {return x.b > y.b; } //按b逆序
int n, sumA, sumb, K, ans, f[N*N][N];
signed main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &c[i].a), sumA += c[i].a;
for(int i = 1; i <= n; i++) scanf("%d", &c[i].b);
sort(c + 1, c + 1 + n, cmp);
while(sumb < sumA) sumb += c[++K].b;
printf("%d ", K);
memset(f, -0x3f, sizeof f), f[0][0] = 0;
for(int i = 1; i <= n; i++)
for(int j = sumb; j >= c[i].b; j--)
for(int k = 1; k <= K; k++)
f[j][k] = max(f[j][k], f[j-c[i].b][k-1] + c[i].a);
for(int j = sumA; j <= sumb; j++) ans = max(ans, f[j][K]);
cout << sumA - ans;
}