[C++]01背包問題


基本問題

有N件物品和一個容量為V 的背包。放入第\(i\)件物品耗費的空間是\(C_i\),得到的價值是\(W_i\)。求解將哪些物品裝入背包可使價值總和最大。

思路

這是最基礎的背包問題,特點是:每種物品僅有一件,可以選擇放或不放。 用子問題定義狀態:即\(F[i,v]\)表示前i件物品恰放入一個容量為v的背包可以獲得的最大價值。則其狀態轉移方程便是:
\(F_{i, v} = \max\{F_{i−1,v}, F_{i−1,v−C_i} + W_i\}\)

核心代碼

memset(F[0], 0, sizeof(F[0]));
for (int i = 1; i <= n; ++i) {
	for (int v = 0; v <= V; ++v) {
		if (v >= c[i]) F[i][v] = max(F[i-1][v], F[i-1][v-c[i]] + w[i]);
		else F[i][v] = F[i-1][v];
	}
}
for (int i = 0; i <= V; ++i) ans = max(ans, F[n][i]);

其時間復雜度和空間復雜度都是\(O(NV)\), 其中時間復雜度基本上不能再優化了,但空間復雜度卻可以優化到\(O(V)\)

memset(F, 0, sizeof(F));
for (int i = 1; i <= n; ++i) {
	for (int v = V; v >= c[i]; --v) {
		F[v] = max(F[v], F[v-c[i]] + w[i]);
	}
}
for (int i = 0; i <= V; ++i) ans = max(ans, F[i]);

求方案數

看一道題:小A點菜
對於這類改變問法的問題,一般只需將狀態轉移方程中的\(max\)改成\(sum\)即可。例如若每件物品均是完全背包中的物品,轉移方程即為
\(F[i,v] = sum\{F[i−1,v],F[i,v−C_i]\}\)

初始條件是\(F[0,0] = 1\)

F[0][0] = 1;
for (int i = 1; i <= n; ++i) {
	for (int v = 0; v <= V; ++v) {
		if (v >= c[i]) F[i][v] = F[i-1][v] + F[i-1][v-c[i]];
		else F[i][v] = F[i-1][v];
	}
}
ans = F[n][V];
printf("%d\n", ans); 

求裝得盡量滿

再看一題:裝箱問題 [NOIp2001普及組第4題]
其實這里\(W_i\)就是\(C_i\)

memset(F, 0, sizeof(F));
for (int i = 1; i <= n; ++i) {
	for (int v = V; v >= c[i]; --v) {
		F[v] = max(F[v], F[v-c[i]] + c[i]);
	}
}
for (int i = 0; i <= V; ++i) ans = max(ans, F[i]);
printf("%d\n", V - ans);

求所有體積可能

又來一題:積木城堡
求出每個高度的城堡數量。

for (int i = 0; i < n; ++i) {
	int np = 0;
	int now;
	int sum = 0;
	while (1) {
		scanf("%d", &now);
		if (now == -1) break;
		a[++np] = now;
		sum += now;
	}
	if (max_sum < sum) max_sum = sum;
	memset(F, 0, sizeof(F));
	F[0] = 1;
	for (int j = 1; j <= np; ++j) {
		for (int v = sum; v >= a[j]; --v) {
			if (F[v-a[j]] && !F[v]) {
				++h[v];
				F[v] = 1;
			}
		}
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM