http://codeforces.com/contest/730/problem/J
3
4 36 1
90 45 40
其实可以知道,选出多少个瓶子呢?是确定的,当然选一些大的。
那么问题转化为:
在n个瓶子中,选出k个,然后把剩余的n - k个瓶子得液体转移过去这k个里面,费用最小。其实就是使得剩余的n - k个瓶子的拥有液体数最小,那么其实就是需要那k个瓶子得拥有液体数最多。
dp[i][k][j]表示在前i个物品中,选出了k个物品,产生的总容量是j的时候,拥有液体数的最大值。
因为选出了k个东西,我们肯定是拥有一个容量的了,那么如果这n件物品,选出任意的k件东西,如果能产生相同的容量,我当然是选一些已经用了最多的,这样n-k个物品,转移液体过来,费时减小。
最后,还需要输出最小代价,就是,比如上面的样例,选容量是90是可以得,选容量是45也可以,那么就看看那个的代价更小了。
不一定容量大的,代价就小,因为可能用的不满。容量小的,就算用得满,也很小。
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #include <assert.h> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; #include <iostream> #include <sstream> #include <vector> #include <set> #include <map> #include <queue> #include <string> const int maxn = 1e2 + 20; struct node { int cur; int tot; } a[maxn]; struct dd { int use; int lef; } dp[maxn][100 * 100 + 20]; void work() { int n; cin >> n; int has = 0; for (int i = 1; i <= n; ++i) { cin >> a[i].cur; has += a[i].cur; } for (int i = 1; i <= n; ++i) { cin >> a[i].tot; } memset(dp, -0x3f, sizeof dp); dp[0][0].use = 0; dp[0][0].lef = 0; for (int i = 1; i <= n; ++i) { for (int k = n; k >= 1; --k) { for (int j = 100 * 100; j >= a[i].tot; --j) { if (dp[k][j].use < dp[k - 1][j - a[i].tot].use + a[i].cur) { dp[k][j].use = dp[k - 1][j - a[i].tot].use + a[i].cur; dp[k][j].lef = j - dp[k][j].use; } } } } for (int k = 1; k <= n; ++k) { bool flag = false; int ans = inf; for (int j = 100 * 100; j >= 1; --j) { if (dp[k][j].use >= 0) { if (dp[k][j].lef >= has - dp[k][j].use) { flag = true; ans = min(ans, has - dp[k][j].use); // cout << k << " " << has - dp[k][j].use << endl; // return; } } } if (flag) { cout << k << " " << ans << endl; return; } } } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif work(); return 0; }