目录
一、前言
咳咳,好久没更新了是吧。
最近呢太懒了(真话)不想看csdn。
所以递归又又又没写,o(* ̄︶ ̄*)o
没事,今天给大家带来一个全新的题目,当然,最近作业里有这道题,上网一查又发现那代码一点都看不懂,所以直接靠自己的理解改成了正常样子。
废活不多说,进入题目。
二、正题
1.题目描述
好,这题是这样的:
——————————————————————
题目描述
给定一个只包含 n 个正整数的非空数组 a1,a2,…,an。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
如果能,输出 Yes;如果不能,输出 No。
输入描述
第 1 行为一个正整数,表示数组的的元素个数 n。
第 2 行为 n 个正整数 a1,a2,⋯,an,表示数组的 n 个元素。
输出描述
一行,如果 a 数组可以分割成两个等和子集,则输出 Yes,反之输出 No。
样例1
输入
4 1 5 11 5
输出
Yes
样例2
输入
4 1 2 3 5
输出
No
提示
样例 1 解释,数组可以分割成[1,5,5]和[11] 。
1≤n≤1000
1≤ai≤1000
——————————————————————————
2.题目解答(原题答案)
这题目一看数据就肯定要用动归哈,但我查了半天,csdn里的答案不是这样的:
#include <vector>
#include <numeric>
using namespace std;
bool canPartition(vector<int>& nums) {
int sum = accumulate(nums.begin(), nums.end(), 0);
if (sum % 2 != 0) return false;
int target = sum / 2;
vector<vector<bool>> dp(nums.size() + 1, vector<bool>(target + 1, false));
for (int i = 0; i <= nums.size(); ++i) {
dp[i][0] = true;
}
for (int i = 1; i <= nums.size(); ++i) {
for (int j = 1; j <= target; ++j) {
if (j - nums[i-1] >= 0) {
dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i-1]];
} else {
dp[i][j] = dp[i-1][j];
}
}
}
return dp[nums.size()][target];
}
就是这样的......:
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
vector<int> dp(10001, 0);
for (int i = 0; i < nums.size(); i++) {
sum += nums[i];
}
// int sum = accumulate(nums.begin(), nums.end(), 0);
if (sum % 2 == 1) return false;
int target = sum / 2;
for(int i = 0; i < nums.size(); i++) {
for(int j = target; j >= nums[i]; j--) {
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
}
}
if (dp[target] == target) return true;
return false;
}
};
对于我这种刚刚学dp的小盆友来说,这太离谱啦!!!!
(注:这代码可以去找其他的文章来看,我这里因为不会就不解说辣)
必须改好吧!!!
3.经本蒟蒻爆改后的答案(一维dp)
我们分析一下题哈:
首先,因为题目中给出要求是否是等和子集,所以我们要用到bool型背包。
那么至于bool背包呢,我到时候专门出个专题来讲解动归知识哈。
首先,他要求等和数组,并且输入都是正整数,我们发现,当这些如果是奇数,那么他根本就不可能!所以可以先加个特判,这时候补上前面的定义输入,就可以得到:
#include <bits/stdc++.h>
using namespace std;
int n,a[1005],sum;
bool f[1000005];
int main(){
cin >> n;
for(int i = 1;i <= n;i++){
cin >> a[i];
sum += a[i];
}
if(sum%2 == 1){
cout << "No";
return 0;
}
}
(其中f大小一定要1e6,因为数据给的是1000*1000)
接下来我们知道,当没有数的时候(也就是n=0时),那不就是对的么,所以
接下来就是背包部分,也就是01背包
我们第一层循环就是次数,直接用1~n就行了
第二层是循环遍历容量,也就是其中任意数量数的和,如果可以装得下就加进来,这个需要基础dp知识,不会先去学学哈
那么位运算就是如果可以那么,否则=0,反正当f(j)=1时或运算就是1了。
我们便能将所有的都情况都遍历的一遍。
原谅本蒟蒻讲的不好,因为dp真要讲起来太麻烦了,所以简单说说不明白,请谅解
那么这个时候我们在遍历一下sum/2的数组里是否是1就行了(因为等和所以要/2)
好的,完整代码:
#include <bits/stdc++.h>
using namespace std;
int n,a[1005],sum;
bool f[1000005];
int main(){
cin >> n;
for(int i = 1;i <= n;i++){
cin >> a[i];
sum += a[i];
}
if(sum%2 == 1){
cout << "No";
return 0;
}
f[0] = 1;
for(int i = 1;i <= n;i++){
for(int j = sum/2;j >= a[i];j--){
f[j] |= f[j-a[i]];
}
}
if(f[sum/2]) cout << "Yes";
else cout << "No";
}
也就这样了,至于动归的写法蒟蒻只能放这里了,各位自己去推理吧(。・_・。)ノI’m sorry~
今天就到这里了,886
945

被折叠的 条评论
为什么被折叠?



