LeetCode#416分割等和子集
题目链接
题面很简单,意思就是给你个数组,然后问能否把这个数组分成加和相等的两个子数组,能的话返回true,否则返回false。首先能想到以下几种一定返回false:
- 如果加和的sum为奇数则一定不能等分
- 如果数组中的元素数量n<2一定不能等分
- 如果数组中有一个元素大于sum/2一定不能等分(为了降低时间复杂度,我们只需要判断数组中最大的数和sum/2的关系即可)
一开始我的想法是使用递归,遍历一遍整个数组,在每个数上判断一下选择或者不选择,但是直接tle了(这样递归的时间复杂度是2^n,我算成n^2了人傻了)
附一个递归的代码:而正确的解法应该是使用动态规划,和我的想法差不多,也是判断每个数选择或者不选择,这样就可以把这道题模型化为01背包1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31class Solution {
public:
void dfs(vector<int>& nums,int pos,int cnt,int& sum,bool& flag)
{
if(cnt == sum)
{
flag = true;
return;
}
if(cnt > sum)
return;
if(pos == nums.size())
return;
dfs(nums,pos+1,cnt+nums[pos],sum,flag);
dfs(nums,pos+1,cnt,sum,flag);
}
bool canPartition(vector<int>& nums) {
int sum = 0;
bool flag = false;
for(int i = 0 ; i < nums.size() ; i++)
sum += nums[i];
if(sum%2==1)
return false;
else
{
sum/=2;
dfs(nums,0,0,sum,flag);
}
return flag;
}
};还是菜没想到
dp[i][j]的含义是当使用数组中的前i个数,能否组合成j
初始化dp数组有几个情况: - 当i=0的时候,即只使用数组中第0个数,dp[0][num[0]]=true
- 当j=0的时候,不选择任何数即可满足条件,所以dp[i][0]=true
dp[i][num[i]]=true也是成立的,但是好像没啥用
动态转移方程分为两种情况: - 选取num[i],dp[i][j] = dp[i-1][j-num[i]]
- 不选取num[i],dp[i][j]=dp[i-1][j]
附代码之前,再记录两个从来没用过的函数,accumulate()和*max_element()。
accumulate()函数在#include
1 | int sum = accumulate(vec.begin() , vec.end() , 42); //求vector容器所有元素的累加和,累加初值设为42 |
还有种另类用法是将整形vector转换为字符串,例如:
1 | string sum = accumulate(v.begin() , v.end() , string(" ")); |
max_element()函数,返回容器中的最大值,相对应的,min_element()函数返回容器/数组中的最小值,它们都在#include
附代码:
1 | class Solution { |