http://codeforces.com/contest/294/problem/B
据说是贪心,我用了一个复杂度是2e8的dp水过去了。
其实这题就是给你n个数,每个数有两个权值,分成两组,使得第一个权值之和,和第二个权值之和的最大值最小。
那么直接设dp[i][j][k][h]表示前i个数中,选了j个,第一个权值的和是k,第二个权值是h,是否可能。
这里是一定要选出n个的,就是n个数都必须使用,才能滚动数组。(把第二维滚动)
如果是从n个数中选出k个,那么就不能滚动了。
那么第一维是01背包直接省略,j那里可以滚动数组。
总复杂度2e8
然后题解是贪心......
还有要注意,k要大于h,就是底下的书要长于上面的书。
#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> struct node { int ti, wi; }a[111]; int dp[2][200 + 2][10000 + 2]; void work() { int n; cin >> n; int DFN = 1; int sumti = 0, sumwi = 0; for (int i = 1; i <= n; ++i) { cin >> a[i].ti >> a[i].wi; sumti += a[i].ti; sumwi += a[i].wi; } if (n == 1) { cout << min(a[1].ti, a[1].wi) << endl; return; } // sort(a + 1, a + 1 + n); dp[0][0][0] = DFN; int now = 0; for (int i = 1; i <= n; ++i) { now = !now; ++DFN; for (int j = sumti; j >= 0; --j) { for (int k = sumwi; k >= 0; --k) { if (j >= a[i].ti && dp[!now][j - a[i].ti][k] == DFN - 1) { dp[now][j][k] = DFN; } if (k >= a[i].wi && dp[!now][j][k - a[i].wi] == DFN - 1) { dp[now][j][k] = DFN; } } } } int ans = inf; for (int i = 0; i <= sumti; ++i) { for (int j = 0; j <= i; ++j) { if (dp[now][i][j] == DFN) { ans = min(ans, max(i, j)); } } } cout << ans << endl; } int main() { #ifdef local freopen("data.txt", "r", stdin); // freopen("data.txt", "w", stdout); #endif work(); return 0; }