Loading

AT_tdpc_knapsack ナップザック

定义:\(A_c\) 表示颜色为 \(c\) 的物品集合,\(g \otimes S\) 表示背包 \(g\) 中插入集合 \(S\) 中的所有物品后的新背包。

刚开始想的是对于每种颜色,求出各自背包,然后进行 \(lim_c\)\(O({lim_w}^2)\) 的合并。

显然 T 了,考虑漏了什么:本来操作 \(g \otimes S\)\(O(|g| \cdot |s|)\) 的,当物品个数小于背包大小时,这种合并方式更加合理。

那么就好办了,设 \(f_{c,k}\) 表示考虑前 \(c\) 种颜色,使用 \(k\) 种颜色时的背包(下文中,前一维滚动掉,但是这样 \(k\) 需要倒序枚举)。

枚举使用 \(c\) 颜色去更新整个 \(f\),则 \(f_k \otimes A_c \to f_{k+1}\)

因为物品总数为 \(n\),所以这么做是 \(O(nm\cdot lim_c)\) 的。

点击查看代码
#include <bits/stdc++.h>

using namespace std;

#ifdef LOCAL
auto debug(auto ...b){((cerr << b << ' ') , ...); cerr << '\n';}
#else
#define debug(...) 0717
#endif

using ll = long long;
using uint = unsigned;

const int INF = 0x3F3F3F3F;
const ll INFl = 0x3F3F3F3F3F3F3F3F;

#define all(x) x.begin(),x.end()
#define r_all(x) x.rbegin(),x.rend()

const int N = 105;
const int S = 10005;
const int C = 53;

using gd = array<int , 2>;

int n , lim_w , lim_c;

vector<gd> a[C];

int f[C][S] , g[S];

signed main() {
#ifdef LOCAL
#elif defined(ONLINE_JUDGE)
#else
#endif
	
	ios::sync_with_stdio(0); cin.tie(0);
	
	cin >> n >> lim_w >> lim_c;
	for(int i = 1; i <= n; ++ i){
		int w , v , c;
		cin >> w >> v >> c;
		a[c].push_back({w , v});
	}
	
	for(int c = 1; c <= 50; ++ c){
		for(int k = lim_c - 1; k >= 0; -- k){
			copy(f[k] , f[k] + lim_w + 1 , g);
			for(auto [w , v] : a[c]) for(int j = lim_w; j >= 0; -- j){
				if(j + w <= lim_w) g[j + w] = max(g[j + w] , g[j] + v);
			}
			for(int j = 0; j <= lim_w; ++ j){
				f[k + 1][j] = max(f[k + 1][j] , g[j]);
			}
		}
	}
	
	int ans = 0;
	for(int k = 1; k <= lim_c; ++ k){
		ans = max(ans , *max_element(f[k] , f[k] + lim_w + 1));
	}
	cout << ans << '\n';
	
	return 0;
}
posted @ 2024-11-25 22:01  TongKa  阅读(15)  评论(0)    收藏  举报