因为比较懒,所以先写个最难题的题解。其他的有空再说吧(可以私信催 ddl 或者听我口胡做法)

J 题题意:完全背包,但是每个物品每次被选后,价值都会产生缩水。

换言之,一个 的 J 题背包,可以等价为这样的 01 背包:

把这个东西用优先队列实现一下,然后跑一遍 01 背包就做完了。

#include <queue>
#include <cstdio>
#define int long long
int init() {
	char c = getchar();
	int x = 0, f = 1;
	for (; c < '0' || c > '9'; c = getchar())
		if (c == '-') f = -1;
	for (; c >= '0' && c <= '9'; c = getchar())
		x = (x << 1) + (x << 3) + (c & 15);
	return x * f;
}
void print(int x) {
	if (x < 0) putchar('-'), x = -x;
	if (x / 10) print(x / 10);
	putchar(x % 10 + '0');
}
const int N = (int) 3e3 + 5;
int dp[N];
int mx(int x, int y) {
	return x > y ? x : y;
}
int w[N * 12], c[N * 12];
std::priority_queue <int> val[N];
signed main() {
	int n = init(), m = init();
	for (int i = 1; i <= n; ++i) {
		int a = init(), b = init();
		val[a].push(b);
	}
	int num = 0;
	for (int j = 1; j <= m; ++j) {
		if (val[j].empty()) continue;
		for (int k = 1; j * k <= m; ++k) {
			int now = val[j].top(); val[j].pop();
			if (now <= 0) break;
			w[++num] = j, c[num] = now;
			val[j].push(now-1);
		}
	}
	for (int i = 1; i <= num; ++i)
		for (int j = m; j >= w[i]; --j)
			dp[j] = mx(dp[j], dp[j - w[i]] + c[i]);
	int ans = 0;
	for (int j = 0; j <= m; ++j)
		ans = mx(ans, dp[j]);
	print(ans), putchar('\n');
}