算法课第15周第1题—— 416. Partition Equal Subset Sum

题目描述:

Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.

Note:

  1. Each of the array element will not exceed 100.
  2. The array size will not exceed 200.

Example 1:

Input: [1, 5, 11, 5]

Output: true

Explanation: The array can be partitioned as [1, 5, 5] and [11].

Example 2:

Input: [1, 2, 3, 5]

Output: false

Explanation: The array cannot be partitioned into equal sum subsets.

程序代码:

解法1:

class Solution {
public:
	bool canPartition(vector<int>& nums) {
		int n = nums.size();
		// 计算数组中数字总和
		int total = 0;
		for (int i = 0; i < n; i++) {
			total += nums[i];
		}
		// 若总和不为偶数,则输出肯定为false
		// (因为计算的数之和需和数组中剩余的另一半和相等)
		if (total % 2 == 1) {
			return false;
		}

		// 用于动态规划
		vector<int> f(total / 2 + 1, 0);

		// 利用背包问题的思想
		// 这里体积v即为数组中的数字大小nums[i],总体积V为total/2
		// 每个数的价值同样为数组中数字大小nums[i]
		// 最终输出时,需要判断f[total/2]是否等于total/2
		for (int i = 0; i < n; i++) {
			for (int v = total / 2; v >= nums[i]; v--) {
				f[v] = max(f[v], f[v - nums[i]] + nums[i]);
			}
		}

		if (f[total / 2] == total / 2) {
			return true;
		}
		else {
			return false;
		}
	}
};

解法2:

class Solution {
public:
	bool canPartition(vector<int>& nums) {
		int n = nums.size();
		// 计算数组中数字总和
		int total = 0;
		for (int i = 0; i < n; i++) {
			total += nums[i];
		}
		// 若总和不为偶数,则输出肯定为false
		// (因为计算的数之和需和数组中剩余的另一半和相等)
		if (total % 2 == 1) {
			return false;
		}

		// 用于动态规划
		vector<bool> f(total / 2 + 1, false);
		f[0] = true;

		// 利用类似背包问题的思想
		// 这里体积v即为数组中的数字大小nums[i],总体积V为total/2
		// 但不考虑价值,而是直接用布尔值判断
		// 因为初始值只有f[0]为true, 所以只有v-nums[i]刚好为0的值,才能在后续计算中得出true
		for (int i = 0; i < n; i++) {
			for (int v = total / 2; v >= nums[i]; v--) {
				f[v] = f[v] || f[v - nums[i]];
			}
		}

		return f[total / 2];
	}
};

简要题解:

本题是一道可以用动态规划解决的问题。

先分析题意,本题的输入是一串非空的正整数数组,要求出是否能将数组分成两部分,这两部分的和相等。

一开始看完题目,我没能一下子想到这题可以用动态规划解决。但仔细思考分析后,可以发现这题是一道比较隐蔽的可以用类似背包问题的思想解决的问题。

首先要计算一下数组中数字总和total, 因为本题其实就是要计算数组中的一些数字和能否为total/2。先判断,若total不为偶数,则直接可以输出false.  接着,这题的隐蔽之处在于,其实可以将数组中的数字nums[i]既看作背包问题中的体积v[i], 又看作价值w[i],  总体积V其实就是数组中数字的总和total的一半,即total/2. 而最终的输出,只需要用动态规划的数组f[i]来判断,若f[total/2] == total/2 ,即占的体积为total/2时刚好价值为total/2 ,则说明为true.     由此可以列出转移方程:

外层循环枚举i = 0 ~n-1, 内层循环枚举v = total / 2 ~ nums[i] : f[v] = max(f[v], f[v - nums[i]] + nums[i]);

       最终,若 (f[total / 2] == total / 2) 则输出true, 否则输出false.

其实后来仔细思考了一下,我还得出了一个更简单的解法二,这个解法就不是完全按照背包问题的解法(不过还是用了类似的思想)。其他都与解法一类似,不过这里的动态规划数组f[i]直接用布尔变量,初始化时除了f[0]为true,其他都为false. 而转移方程中:

f[v] = f[v] || f[v - nums[i].   

这样,因为初始值只有f[0]为true, 所以只有v-nums[i]刚好为0的值(不多也不少),才能在后续计算中得出true。


本题是一道比较巧妙的背包问题,其比较隐蔽,难以一下子发现,不过想出来之后,套用背包问题的思路就相对容易多了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值