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