动态规划 第5讲: 01背包问题&空间简化 (LeetCode刷题1: 降维、二维、三维)

前两天,写了一篇文章:动态规划 第4讲: 01背包问题&空间简化 (基础入门篇),作为初步了解,接下来做几道题来巩固一下,每道题都有最初版和空间优化版的代码。

一、416. 分割等和子集 (难易程度:medium)

原题链接:https://leetcode-cn.com/problems/partition-equal-subset-sum

给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200

示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].

示例 2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.

空间优化前的代码:
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        if(nums.size()<=1) return false;//只有一个或者没有,不可分割
        int n=nums.size(), half=0;
        for(int i=0; i<n; i++) half+=nums[i];
        //和为奇数时,不可能划分成两个和相等的集合
        if(half%2==1) return false;
        half/=2;
        //dp数组保留第0行和第0列,预防分类讨论
        vector<vector<bool>>dp(n+1, vector<bool>(half+1, false));
        dp[0][0]=true; //初始值为true
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=half; j++)
            {
                if(nums[i-1]<=j)
                {
                    //选当前的或者不选当前的
                    dp[i][j]= dp[i-1][j]+dp[i-1][j-nums[i-1]];
                }
                else
                {
                    //不能选当前的情况
                    dp[i][j]= dp[i-1][j];
                }
            }
        }
        return dp[n][half];
    }
};
数组降维(二维变一维)之后的代码:
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        if(nums.size()<=1) return false;//只有一个或者没有,不可分割
        int n=nums.size(), half=0;
        for(int i=0; i<n; i++) half+=nums[i];
        if(half%2==1) return false;
        half/=2;
        vector<bool>dp(half+1, false);
        dp[0]=true; //初始值为true
        for(int i=1; i<n; i++)
        {
            //易错:数组降维,要从大到小算起
            for(int j=half; j>=0; j--)
            {
                if(j>=nums[i])
                {
                    dp[j]=dp[j]+dp[j-nums[i]];
                }
            }
        }
        return dp[half];
    }
};
474. 一和零(难易程度:medium)

原题链接:https://leetcode-cn.com/problems/ones-and-zeroes/

在计算机界中,我们总是追求用有限的资源获取最大的收益。
现在,假设你分别支配着 m 个 0 和 n 个 1。另外,还有一个仅包含 0 和 1 字符串的数组。
你的任务是使用给定的 m 个 0 和 n 个 1 ,找到能拼出存在于数组中的字符串的最大数量。每个 0 和 1 至多被使用一次。

注意:
给定 0 和 1 的数量都不会超过 100。
给定字符串数组的长度不会超过 600。

示例 1:
输入: Array = {“10”, “0001”, “111001”, “1”, “0”}, m = 5, n = 3
输出: 4
解释: 总共 4 个字符串可以通过 5 个 0 和 3 个 1 拼出,即 “10”,“0001”,“1”,“0” 。

示例 2:
输入: Array = {“10”, “0”, “1”}, m = 1, n = 1
输出: 2
解释: 你可以拼出 “10”,但之后就没有剩余数字了。更好的选择是拼出 “0” 和 “1” 。

空间优化前的代码:

先大致说一下解题思路,这里如果不优化, 需要用三维数组、四层循环,先写一下优化之前的代码:
(1)
dp[i][j][k]表示:前i个字符串,用j个0和k个1一共最多可以拼出多少个字符串(小于等于i)。
(2)
dp[i][j][k]要么等于dp[i-1][j][k]:即第i个字符串我不看了,和之前i-1个字符串用j个0、k个1的结果一样就行了;
要么拼出第i个字符串,则该字符串要使用x个0和y个1。那么dp[i][j][k]=dp[i-1][j-x][k-y]:截止前i-1个字符串、使用j-x个0、k-y个1的结果。

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        int dp[601][101][101]={0};
        int ssize= strs.size();
        for(int i=1; i<=ssize; i++)
        {
            for(int j=0; j<=m; j++)
            {
                for(int k=0; k<=n; k++)
                {
                    int zero=0, one=0;
                    //计算0和1的个数
                    for(const char& c: strs[i-1])
                    {
                        if(c=='0') zero++;
                        else one++;
                    }
                    
                    if(zero<=j && one<=k)
                    {
                        //如果可以选择,(1)不选,(2)选了,然后加1
                        dp[i][j][k]= max(dp[i-1][j][k], dp[i-1][j-zero][k-one]+1);
                    }
                    else
                    {
                        dp[i][j][k]=dp[i-1][j][k];
                    }
                }
            }
        }
        return dp[ssize][m][n];
    }
};
数组降维(三维变二维)之后的代码:

降维后,我把三维降成二维,把四重循环减少维四重循环:

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<vector<int>>dp(m+1, vector(n+1, 0));
        int ssize= strs.size();
        for(int i=0; i<ssize; i++)
        {
            int zero=0, one=0;
            //计算0和1的个数
            //循环前,先求当前的0和1的个数,可以减少一层循环
            for(const char& c: strs[i])
            {
                if(c=='0') zero++;
                else one++;
            }

            for(int j=m; j>=zero; j--)
            {
                for(int k=n; k>=one; k--)
                {
                   //如果可以选择,(1)不选,(2)选了,然后加1
                    dp[j][k]= max(dp[j][k], dp[j-zero][k-one]+1);
                }
            }
        }
        return dp[m][n];
    }
};
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页