2021 ICPC区域赛(上海)


比赛链接:

https://ac.nowcoder.com/acm/contest/24872

I:

\(n\) 件物品,第 \(i\) 件物品的体积是 \(t_i\),价值是 \(v_i\),最多选出 \(k\) 件使它们的体积翻倍,然后选出若干件物品将其分为 体积和 相同的两堆,求选出物品的价值之和最大值

思路:

\(n\) 件物品中选出若干件物品,可以想到 背包
由于要分成 两堆,我们可以假定,放入 第一堆 中是加入背包,放入 第二堆 中是拿出背包,也就是说放入第一堆中是 增加体积,放入第二堆中 减少体积,不论放入哪一堆,价值 都是增加的。
dp[i][j][p]为 \(dp\) 方程,\(i\) 表示当前选择的是第几件物品, \(j\) 表示选中几件物品使其 体积翻倍\(p\)背包体积,就是一个简单背包啦。
可以发现,在求解的过程中,每一个物品只需要从上一个物品的状态转移过来,于是我们可以通过 滚动数组 优化掉 第一维度

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 105;
const int W = 1310;
LL n, k, w[N], v[N], f[N][W * 2], g[N][W * 2], ans = -(1LL << 60);
int main(){
	scanf("%lld %lld", &n, &k);
	for (int i = 1; i <= n; i++)
		scanf("%lld %lld", &v[i], &w[i]);
	memset(f, 233, sizeof(f));
	f[0][W] = 0;
	for (int i = 1; i <= n; i++){
		memcpy(g, f, sizeof(g));
		for (int j = 0; j < i && j <= k; j++)
			for (int p = 0; p < W * 2; p++){
				if (p > w[i]) g[j][p - w[i]] = max(g[j][p - w[i]], f[j][p] + v[i]);
				if (p > 2 * w[i]) g[j][p - 2 * w[i]] = max(g[j][p - 2 * w[i]], f[j][p] + v[i]);
				if (p + w[i] < W * 2) g[j][p + w[i]] = max(g[j][p + w[i]], f[j][p] + v[i]);
				if (p + 2 * w[i] < W * 2) g[j][p + 2 * w[i]] = max(g[j][p + 2 * w[i]], f[j][p] + v[i]);
			}
		memcpy(f, g, sizeof(f));
	}
	for (int i = 0; i <= k; i++)
		ans = max(ans, f[i][W]);
	cout << ans << "\n";
	return 0;
}


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM