动态规划法求解完全背包问题

问题描述

有n种重量和价值分别为wi、vi(1≤i≤n)的物品,从这些物品中挑选总重量不超过W的物品,求出挑选物品价值总和最大的挑选方案,这里每种物品可以挑选任意多件。

问题求解

设置动态规划二维数组dp,dp[i][j]表示从前i个物品中选出重量不超过j的物品的最大总价值。
显然有边界条件:dp[i][0]=0(背包不能装入任何物品时,总价值为0),dp[0][j]=0(没有任何物品可装入时,总价值为0)→采用memset函数一次性初始化为0。
另外设置二维数组fk,其中fk[i][j]存放dp[i][j]得到最大值时物品i挑选的件数。
状态转移方程:
动态规划法求解完全背包问题_第1张图片
这样,dp[n][W]便是背包容量为W、考虑所有n个物品(同一物品允许多次选择)后得到的背包最大总价值,即问题的最优结果。

代码

int n, W;
int w[MAXN], v[MAXN];
int dp[MAXN + 1][MAXN + 1], fk[MAXN + 1][MAXN + 1];

int solve()
{
	int i, j, k;
	for (i = 1; i <= n; i++)
	{
		for (j = 0; j <= W; j++)
		{
			for (k = 0; k*w[i] <= j; k++)
			{
				if (dp[i][j] < dp[i - 1][j - k * w[i]] + k * v[i])
				{
					dp[i][j] = dp[i - 1][j - k * w[i]] + k * v[i];
					fk[i][j] = k;
				}
			}
		}
	}
	return dp[n][W];
}

void Traceback()
{
	int i = n, j = W;
	while (i >= 1)
	{
		cout << "物品" << i << "共" << fk[i][j] << "件";
		j -= fk[i][j] * w[i];
		i--;
	}
}

算法分析

solve算法有三重循环,k的循环最坏可能从0到W,所以算法的时间复杂度为O(nW2)。

算法改进

实际上,上述算法中不必使用k循环,可以修改为在挑选物品i时直接多次重复挑选。
因为计算dp[i][j]中选择k(k≥1)个的情况与在dp[i][j-w[i]]的计算中选择k-1个的情况是相同的,所以dp[i][j]的递推中k≥1部分的计算已经在dp[i][j-w[i]]的计算中完成了。

int n, W;
int w[MAXN], v[MAXN];
int dp[MAXN + 1][MAXN + 1], fk[MAXN + 1][MAXN + 1];

int solve()
{
	int i, j, k;
	for (i = 1; i <= n; i++)
	{
		for (j = 0; j <= W; j++)
		{
			if (j < w[i])
				dp[i][j] = dp[i - 1][j];
			else
				dp[i][j] = max(dp[i - 1][j], dp[i][j - w[i]] + v[i]);
		}
	}
	return dp[n][W];
}

该算法的时间复杂度为O(nW)。

你可能感兴趣的:(算法)