474. 一和零

题目:

在计算机界中,我们总是追求用有限的资源获取最大的收益。

现在,假设你分别支配着 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" 。

 

解答:

三维dp:

dp[i][j][k]表示:前i个字符串,用j个0和k个1一共最多可以拼出多少个字符串(小于等于i)。

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:
    pair<int,int> calculate(const string& str){
        pair<int,int> res={0,0};
        for(const char& c:str){
            if(c=='0'){
                res.first+=1;
            }
            else{
                res.second+=1;
            }
        }
        return res;
    }
    int findMaxForm(vector<string>& strs, int m, int n) {
        int dp[601][101][101]={0};
        int strCnt=strs.size();
        for(int seq=1;seq<=strCnt;++seq){
            for(int i=0;i<=m;++i){
                for(int j=0;j<=n;++j){
                    dp[seq][i][j]=dp[seq-1][i][j];
                    auto pair=calculate(strs[seq-1]);
                    if(i>=pair.first and j>=pair.second){
                        dp[seq][i][j]=max(dp[seq][i][j],dp[seq-1][i-pair.first][j-pair.second]+1);
                    }
                }
            }
        }
        return dp[strCnt][m][n];
    }
};

 

 

然后发现dp第i层时只用到第i-1层的数据,那么可以利用滚动数组来压缩空间,缩减为二维dp:

class Solution {
public:
    pair<int,int> calculate(const string& str){
        pair<int,int> res={0,0};
        for(const char& c:str){
            if(c=='0'){
                res.first+=1;
            }
            else{
                res.second+=1;
            }
        }
        return res;
    }
    int findMaxForm(vector<string>& strs, int m, int n) {
        int dp[101][101]={0};
        int strCnt=strs.size();
        pair<int,int> pair={0,0};
        for(int seq=1;seq<=strCnt;++seq){
            for(int i=m;i>=0;--i){
                for(int j=n;j>=0;--j){
                    pair=calculate(strs[seq-1]);
                    if(i>=pair.first and j>=pair.second){
                        dp[i][j]=max(dp[i][j],dp[i-pair.first][j-pair.second]+1);
                    }
                }
            }
        }
        return dp[m][n];
    }
};

 

 好像没多大变化。。

然后我发现我在每次最内层嵌套的循环里计算的当前字符串str[i]的0、1个数,一个一样的结果算了101*101遍。。

就是这里:

 

 

赶紧优化一下,顺便用二维数组来替代pair:

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        int dp[101][101]={0};
        int strCnt=strs.size();
        for(int seq=1;seq<=strCnt;++seq){
            int p[2]={0};
            for(const char& c:strs[seq-1]){
                if(c=='0'){
                    p[0]+=1;
                }
                else{
                    p[1]+=1;
                }
            }
            for(int i=m;i>=p[0];--i){
                for(int j=n;j>=p[1];--j){
                    dp[i][j]=max(dp[i][j],dp[i-p[0]][j-p[1]]+1);
                }
            }
        }
        return dp[m][n];
    }
};

 

 完美!

posted @ 2020-04-25 12:17  NeoZy  阅读(195)  评论(0编辑  收藏  举报