使用單調隊列優化的 O(nm) 多重背包算法


我搜索了一下,找到了一篇很好的博客,講的挺詳細:鏈接

 

解析

多重背包的最原始的狀態轉移方程:

令 c[i] = min(num[i], j / v[i])

f[i][j] = max(f[i-1][j-k*v[i]] + k*w[i])     (1 <= k <= c[i])  這里的 k 是指取第 i 種物品 k 件。

如果令 a = j / v[i] , b = j % v[i] 那么 j = a * v[i] + b.

這里用 k 表示的意義改變, k 表示取第 i 種物品的件數比 a 少幾件。

那么 f[i][j] = max(f[i-1][b+k*v[i]] - k*w[i]) + a*w[i]      (a-c[i] <= k <= a)

可以發現,f[i-1][b+k*v[i]] - k*w[i] 只與 k 有關,而這個 k 是一段連續的。我們要做的就是求出 f[i-1][b+k*v[i]] - k*w[i] 在 k 取可行區間內時的最大值。

這就可以使用單調隊列優化。

 

代碼

其中 Q1 是一個用來存儲可用狀態的隊列, Q2 是單調隊列。

//f[i][j] = max(f[i-1][b+k*v[i]] - k*w[i]) + a*w[i]   (a-c[i] <= k <= a)

for (int i = 1; i <= n; ++i) {
	Ni = Num[i]; Vi = V[i]; Wi = W[i];
	for (int j = 0; j < Vi; ++j) {
		Head1 = Tail1 = 0;
		Head2 = Tail2 = 0;
		Cnt = 0;
		for (int k = j; k <= m; k += Vi) {
			if (Tail1 - Head1 == Ni + 1) {
				if (Q2[Head2 + 1] == Q1[Head1 + 1]) ++Head2;
				++Head1;
			}
			t = f[k] - Cnt * Wi;
			Q1[++Tail1] = t;
			while (Head2 < Tail2 && Q2[Tail2] < t) --Tail2;
			Q2[++Tail2] = t;
			f[k] = Q2[Head2 + 1] + Cnt * Wi; 
			++Cnt;
		}
	}
}

 

例題:HDOJ - 1171

 


免責聲明!

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



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