Gym101064L The Knapsack problem

发布时间 2023-10-16 14:36:37作者: zltzlt

CF 传送门

发现物品的体积很小,尝试从此处入手。

\(K\) 为最大的物品体积。把背包体积 \(m\) 分成差不超过 \(K\) 的两部分,然后合并。这样需要求出 \(f(\frac{m}{2} - K \sim \frac{m}{2} + K)\)

递归地,可以发现需要求出 \(f(\frac{m}{2^k} - K \sim \frac{m}{2^k} + K)\)。最后再合并即可。

code
// Problem: L. The Knapsack problem
// Contest: Codeforces - 2016 USP Try-outs
// URL: https://codeforces.com/gym/101064/problem/L
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 2020;

ll n, m, K, a[maxn], b[maxn], c[maxn], f[maxn], g[99][maxn];

void solve() {
	scanf("%lld%lld", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%lld%lld", &a[i], &b[i]);
		K = max(K, a[i]);
	}
	int tot = 0;
	while (m) {
		c[++tot] = m - K;
		m >>= 1;
	}
	for (int i = 1; i <= n; ++i) {
		for (int j = a[i]; j <= K * 2; ++j) {
			f[j] = max(f[j], f[j - a[i]] + b[i]);
		}
	}
	for (int i = tot; i; --i) {
		for (int j = c[i]; j <= c[i] + K * 2; ++j) {
			if (j <= K * 2) {
				g[i][j - c[i]] = (j < 0 ? 0 : f[j]);
			} else {
				for (int k = (j - K) / 2; k <= j / 2; ++k) {
					g[i][j - c[i]] = max(g[i][j - c[i]], g[i + 1][j - k - c[i + 1]] + g[i + 1][k - c[i + 1]]);
				}
			}
		}
	}
	printf("%lld\n", g[1][K]);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}