leekcode 416.分配礼物(动态规划入门版)

目录

一、前言

二、正题

1.题目描述

2.题目解答(原题答案)

3.经本蒟蒻爆改后的答案(一维dp)

一、前言

咳咳,好久没更新了是吧。

最近呢太懒了(真话)不想看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背包呢,我到时候专门出个专题来讲解动归知识哈。

首先,他要求等和数组,并且输入都是正整数,我们发现,当这些sum(a_1,...,a_n)如果是奇数,那么他根本就不可能!所以可以先加个特判,这时候补上前面的定义输入,就可以得到:

#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时),那不就是对的么,所以f(0) = 1

接下来就是背包部分,也就是01背包

我们第一层循环就是次数,直接用1~n就行了

第二层是循环遍历容量,也就是其中任意数量数的和,如果可以装得下就加进来,这个需要基础dp知识,不会先去学学哈

那么位运算就是如果可以那么f(j) = 1,否则=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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值