1. 程式人生 > 其它 >DTSE Tech Talk 第13期:Serverless憑什麼被譽為未來雲端計算正規化?

DTSE Tech Talk 第13期:Serverless憑什麼被譽為未來雲端計算正規化?

題目連結:https://www.acwing.com/problem/content/5/

題目描述

有 N 種物品和一個容量是 V 的揹包。
第 i 種物品最多有 si 件,每件體積是 vi,價值是 wi。
求解將哪些物品裝入揹包,可使物品體積總和不超過揹包容量,且價值總和最大。
輸出最大價值。

輸入描述

第一行兩個整數,N,V,用空格隔開,分別表示物品種數和揹包容積。
接下來有 N 行,每行三個整數 vi,wi,si,用空格隔開,分別表示第 i 種物品的體積、價值和數量。

輸出描述

輸出一個整數,表示最大價值。
0<N≤1000
0<V≤2000
0<vi,wi,si≤2000

示例

輸入

4 5
1 2 3
2 4 1
3 4 3
4 5 2

輸出

10

分析

約定

我們記第i件物品的體積,價值和數量為v[i],w[i],s[i]

狀態表示

f(i,j)表示滿足如下條件的方案的最大價值:

  • 僅從前i件物品中選擇
  • 所選取的物品體積小於等於j

狀態劃分

對於f(i,j),存在兩大種情況:

  • 沒選第i件物品
  • 選了第i件物品,要考慮選了幾件,1、2、3直到上限都應該考慮

狀態轉移方程

對應狀態劃分,我們便可以得到相應的狀態轉移方程:

  • f(i,j)=f(i-1,j)
  • f(i,j)=max(f[i-1][j-k*v[i]]+k*w[i])   (k*v[i]<=j,k<=s[i])

注意,第二行方程中的第一維為i-1,因為該種大情況將選了幾件第i件物品剝離開再求和處理的。

顯然,我們要取的f(i,j)應當是兩大種情況的最大值,將二者形式統一,最終的狀態轉移方程如下:
f(i,j)=max(f(i-1,j-k*v[i])+k*w[i])   (k*v[i]<=j,k<=s[i])

總結與思考

對於多重揹包問題,基本的解決思路與完全揹包相同,這時我們會理所當然向著完全揹包的優化思路上走,究竟行不行呢?我們來試一試。
按照完全揹包的優化思路,我們尋找狀態轉移方程的特性:

  • f(i,j)=max(f(i-1,j),f(i-1,j-v[i])+w[i],f(i-1,j-2*v[i])+2*w[i],f(i-1,j-3*v[i])+3*w[i],···,f(i-1,j-s[i]*v[i])+s[i]*w[i]
    )
  • f(i,j-v[i])=max(f(i-1,j-v[i]),f(i-1,j-2*v[i])+w[i],f(i-1,j-3*v[i])+2*w[i],···,f(i-1,j-s[i]*v[i])+s[i]*w[i],f(i-1,j-(s[i]+1)*v[i])+(s[i]+1)*w[i])

注意加粗部分,完全揹包中加粗部分項數一致,每項均差w[i],但觀察上面兩個式子,項數是不一致的,無法得到下面的狀態轉移方程:
f(i,j)=max(f(i-1,j)+f(i,j-v[i])+w[i])   (j-v[i]>=0)
因此,老的優化思路是走不通的。

二進位制優化

二進位制優化是多重揹包的一個經典優化方法。
對於第i件物品,我們將其分組為1、2、4、8、···、2^n,最後一組如果不夠2的整數次冪則直接單算一組。
易證,對於任意選擇小於等於s[i]件第i件物品的方案,都可以通過上述分組的組合等價得到。
將上述每一個分組都當作一個物品,該物品體積為該組物品體技之和,價值為該組物品價值之和,這樣就將多重揹包問題轉化為了0-1揹包問題,從而將時間複雜度從NVS降低到了NVlogS(大致表示)。

樸素解法AC程式碼


#include <iostream>
#include <algorithm>

using namespace std;

const int N = 101;
int n, V;
int v[N], w[N], s[N];
int f[N][N];

int main()
{
	cin >> n >> V;

	for (int i = 1; i <= n; i++)
		cin >> v[i] >> w[i] >> s[i];

	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= V; j++)
		{
			for (int k = 0; k <= s[i] && k * v[i] <= j; k++)
			{
				f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]);
			}
		}
	}

	cout << f[n][V] << endl;
	return 0;
}

二進位制優化AC程式碼


#include <iostream>
#include <algorithm>

using namespace std;

const int N = 13000;
int n, V;
int v[N], w[N];
int f[N];

int main()
{
	cin >> n >> V;
	int cnt = 0;
	for (int i = 1; i <= n; i++)
	{
		int a, b, c;
		cin >> a >> b >> c;
		int k = 1;
		while (k <= c)
		{
			cnt++;
			v[cnt] = k * a;
			w[cnt] = k * b;
			c -= k;
			k *= 2;
		}
		if (c)
		{
			cnt++;
			v[cnt] = c * a;
			w[cnt] = c * b;
		}
	}
	n = cnt;
	for (int i = 1; i <= n; i++)
	{
		for (int j = V; j >= v[i]; j--)
		{
			f[j] = max(f[j], f[j - v[i]] + w[i]);
		}
	}

	cout << f[V] << endl;
	return 0;
}