这个题目题意很好理解,说的是将n个水甁中剩余的水全部倒进k个瓶子中。其中,让k最小,而且所需要倒的水最少。简单的说,就是在保证k最小的前提下,让k个瓶子中原有的水量之和最大。
想这个题目的时候也想了好一会,其实看清本质,就是一个0-1背包的问题。首先,k值最小不难确定,这里就不再多说。确定k值后,就是要确定出,这个n个瓶子中,选择哪k个才是符合要求的。对于每个瓶子,无非就是选还是不选的问题。那么,这个就时典型的0-1背包了。对于第i个,只需要考虑选还是不选所造成的影响(这里表现为所得到的不需要移动的水的体积),然后二者取一个最大值即可。这里,对于选还是不选,我门需要根据当前状态判定一下,选i是不是必须的。如果不是,才去判断是否需要选者。判断的方法也很简单,假设当前的状态为i, j, v(前i个中选者j个瓶子来装v体积的水),那么需要判断一下前i个瓶子中,最大的j个瓶子能不能装下v体积的水即可。代码如下,由于使用记忆化搜索,所以比较好理解。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAX = 101;
struct Node{
int a, b;
bool operator < (const Node& args) const{
return b < args.b;
}
}p[MAX];
int c[MAX][MAX], d[MAX][MAX][MAX*MAX];
int dfs(int n, int k, int v){
//printf("%d, %d, %d, %d\n", n, k, v, c[n-1][k]);
if (k > n) return 0;
if (k <= 0) return 0;
if (n < 1) return 0;
if (v <= 0) return 0;
if (d[n][k][v] != -1) return d[n][k][v];
d[n][k][v] = dfs(n-1, k-1, v-p[n].b) + p[n].a;
if (c[n-1][k] >= v){
d[n][k][v] = max(d[n][k][v], dfs(n-1, k, v));
}
return d[n][k][v];
}
int main(){
int n;
while (scanf("%d", &n) != EOF){
int v = 0;
for (int i = 1; i<=n; i++){
scanf("%d", &p[i].a);
v += p[i].a;
}
for (int i = 1; i<=n; i++){
scanf("%d", &p[i].b);
}
sort(p+1, p+n+1);
//for (int i = 1; i<=n; i++) printf("%d %d %d\n",i, p[i].a, p[i].b);
int k = 0, tmp = 0;
for(int i = n; i>=1 && tmp < v; i--) {
tmp += p[i].b;
k++;
}
memset(c, 0, sizeof(c));
memset(d, -1, sizeof(d));
for (int i = 1; i<=n; i++){
c[i][1] = p[i].b;
for (int j = 2; j<=i; j++){
c[i][j] = c[i][j-1] + p[i-j+1].b;
}
}
/*for (int i = 1; i<=n; i++){
for (int j = 1; j<=i; j++){
printf("%d ", c[i][j]);
}
printf("\n");
}*/
printf("%d %d\n", k, v - dfs(n, k, v));
}
}