代码随想录算法训练营第三十六天 | 动规

35 阅读3分钟

LeetCode 1049.最后一块石头的重量II

📖 考察点

动规

📖 题意理解

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0

💡 解题思路

/**

  • 1.dp[i][j] 代表 在rang[0,i] 和容量小于等于j的最大值
  • 2.dp[i][j] = dp[j] = Math.max(dp[i-1][j], dp[i-1][j - stones[i]] + stones[i]);
  • 3.初始化第一行的数值
  • 4.便历顺序为从上到下,从左到右;

*/

💻 代码实现

JavaScript

var lastStoneWeightII = function (stones) {
	if (stones.length === 1) {
		return stones[0];
	}
	let sum = stones.reduce((p, c) => p + c);
	let target = ~~(sum / 2) + 1;
	let dp = new Array(target).fill(0);
	for (let j = stones[0]; j < target; j++) {
		dp[j] = stones[0];
	}
	for (let i = 1; i < stones.length; i++) {
		for (let j = target - 1; j > 0; j--) {
			if (j >= stones[i]) {
				dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
			}
		}
	}
	return Math.abs(sum - dp[target - 1] * 2);
};


LeetCode 494.目标和

📖 考察点

动规

📖 题意理解

💡 解题思路

/**

  • 1.dp[i][j]代表在rang[0,i]里容量为j的背包刚好填满的组合数
  • 2.dp[i][j] = dp[i-1][j] + dp[j-nums[i]];
  • 3.初始化第一行和第一列
  • 4.便历顺序为从上到下,从左到右;

*/

💻 代码实现

JavaScript

var findTargetSumWays = function (nums, target) {
	let sum = nums.reduce((p, c) => p + c);
	if (((sum + target) & 1) === 1 || Math.abs(target) > sum) {
		return 0;
	}
	let half = (sum + target) / 2;
	let dp = new Array(half + 1).fill(0);
	if (nums[0] <= half) {
		dp[nums[0]] = 1;
	}
	let numZero = 0;
	if (nums[0] === 0) {
		dp[0] = 2;
		numZero++;
	} else {
		dp[0] = 1;
	}

	for (let i = 1; i < nums.length; i++) {
		for (let j = half; j > 0; j--) {
			if (nums[i] > j) {
				dp[j] = dp[j];
			} else {
				dp[j] += dp[j - nums[i]];
			}
		}
		if (nums[i] === 0) {
			numZero++;
			dp[0] = 2 ** numZero;
		}
	}
	return dp[half];
};

关键点总结

初始化时如果值为0的情况需要关注

LeetCode474.一和零

📖 考察点

动规 01背包

📖 题意理解

💡 解题思路

/**

  • 1.dp[i][j][k] 代表 range[0,i]最多有j个0和k个1的最长子集长度
  • 2.dp[i][j][k] = Math.max(dp[i - 1][j][k], dp[i - 1][j - num1][k - num2] + 1);
  • 3.初始化i为0时的数组
  • 4.便历顺序为从上到下,从左到右;

*/

💻 代码实现

JavaScript

var findMaxForm = function (strs, m, n) {
	let dp = new Array(strs.length)
		.fill(0)
		.map(() => new Array(m + 1).fill(0).map(() => new Array(n + 1).fill(0)));
	let [num1, num2] = get01Num(strs[0]);
	// 初始化
	if (num1 <= m && num2 <= n) {
		for (let j = num1; j <= m; j++) {
			for (let k = num2; k <= n; k++) {
				dp[0][j][k] = 1;
			}
		}
	}
	for (let i = 1; i < strs.length; i++) {
		let [num1, num2] = get01Num(strs[i]);
		for (let j = 0; j <= m; j++) {
			for (let k = 0; k <= n; k++) {
				if (num1 <= j && num2 <= k) {
					dp[i][j][k] = Math.max(dp[i - 1][j][k], dp[i - 1][j - num1][k - num2] + 1);
				} else {
					dp[i][j][k] = dp[i - 1][j][k];
				}
			}
		}
	}
	return dp[strs.length - 1][m][n];
};

function get01Num(str) {
	let num0 = 0,
		num1 = 0;
	for (let i = 0; i < str.length; i++) {
		if (str[i] === "1") {
			num1++;
		} else {
			num0++;
		}
	}
	return [num0, num1];
}