完全背包:经典DP问题( 基本法/滚动数组法 )

        完全背包问题,在已知物品个数( i )和最大容量( j )后,从i个物品中选取最大价值总和。(每个物品可以选取无数次)

        与01背包问题所不同的就在这个每个物品可以选取多次上了,原有的状态转移方程加上选取当前物品的个数变量( knd ),即在满足k[ind]<=jnd时,状态转移方程为dp[ind][jnd]=Max(dp[ind-1][jnd-knd*k]+knd*value,dp[ind-1][jnd]),0<=knd*k<=jnd。与01背包问题相同,k[ind]>ind时,dp[ind][jnd]=dp[ind-1][jnd]。

        稍稍改动01背包基本法即可。

#include
#define Max(a,b) (a>b?a:b)
int dp[1005][10005];
int k[1005], value[1005];
int main(int argc, char* argv[])
{
	int i, j, s;
	scanf("%d %d", &i, &j);
	for (int ind = 1; ind <= i; ++ind)
		scanf("%d %d", &k[ind], &value[ind]);
	for (int ind = 1; ind <= i; ++ind)
	{
		for (int jnd = 1; jnd <= j; ++jnd)
		{
			if (jnd - k[ind] < 0)
				dp[ind][jnd] = dp[ind - 1][jnd];
			else
			{
				s = dp[ind - 1][jnd];
				for (int knd = 1; knd*k[ind] <= jnd; knd++)
					s = Max(s, dp[ind - 1][jnd - knd * k[ind]] + knd * value[ind]);
				dp[ind][jnd] = s;
			}
		}
	}
	printf("%d\n", dp[i][j]);
	return 0;
}

        在递增knd求取最大值的时候,必须要另建一个变量,否则可能出现都比dp[ind-1][jnd]大,导致dp[ind][jnd]最终值是knd最大时的价值而非价值最大的情况!

        滚动数组法改起来就更简单了,01背包问题时必须从后往前滚动,从而保证每次都是历史数据。完全背包问题只需反过来,从前往后滚动即可,这样每次滚动用到的dp[ind-k]就都是最新的数据了,也就是从可以取当前物品更新最大值的最低容量开始更新最大值,虽然每次只能在原有基础上加上一个当前物品,但是小容量的背包已经根据当前物品更新过了最大值,所以较大容量的背包如果满足更新最大值条件,也就是对应的容量加上了最合适数目的当前物品。

#include
#include
#include
#define Max(a,b) (a>b?a:b)
int dp[10005];
int main(int argc, char* argv[])
{
	int i, j;
	scanf("%d %d", &i, &j);
	while (i--)
	{
		int k, value;
		scanf("%d %d", &k, &value);
		for (int ind = k; ind <= j; ind++)
			dp[ind] = Max(dp[ind - k] + value, dp[ind]);
	}
	printf("%d\n", dp[j]);
	return 0;
}

    滚动数组法比基本法写起来省力,复杂度也低,是基础背包问题首选方法。

你可能感兴趣的:(DP)