474. 一和零

这道题和经典的背包问题非常相似,但是和经典的背包问题只有一种容量不同,这道题有两种容量,即选取的字符串子集中的 \(0\)\(1\) 的数量上限。

定义三维数组 \(\textit{dp}\),其中 \(\textit{dp}[i][j][k]\) 表示在前 \(i\) 个字符串中,使用 \(j\)\(0\)\(k\)\(1\) 的情况下最多可以得到的字符串数量。假设数组 \(\textit{str}\) 的长度为 \(l\),则最终答案为 \(\textit{dp}[l][m][n]\)

当没有任何字符串可以使用时,可以得到的字符串数量只能是 \(0\),因此动态规划的边界条件是:当 \(i=0\) 时,对任意 \(0 \le j \le m\)\(0 \le k \le n\),都有 \(\textit{dp}[i][j][k]=0\)

\(1 \le i \le l\) 时,对于 \(\textit{strs}\) 中的第 \(i\) 个字符串(计数从 \(1\) 开始),首先遍历该字符串得到其中的 \(0\)\(1\) 的数量,分别记为 \(\textit{zeros}\)\(\textit{ones}\),然后对于 \(0 \le j \le m\)\(0 \le k \le n\),计算 \(\textit{dp}[i][j][k]\) 的值。

\(0\)\(1\) 的容量分别是 \(j\)\(k\) 时,考虑以下两种情况:

如果 \(j < \textit{zeros}\)\(k < \textit{ones}\),则不能选第 \(i\) 个字符串,此时有 \(\textit{dp}[i][j][k] = \textit{dp}[i - 1][j][k]\)

如果 \(j \ge \textit{zeros}\)\(k \ge \textit{ones}\),则如果不选第 \(i\) 个字符串,有 \(\textit{dp}[i][j][k] = \textit{dp}[i - 1][j][k]\),如果选第 \(i\) 个字符串,有 \(\textit{dp}[i][j][k] = \textit{dp}[i - 1][j - \textit{zeros}][k - \textit{ones}] + 1\)\(\textit{dp}[i][j][k]\)的值应取上面两项中的最大值。

因此状态转移方程如下:

\[\textit{dp}[i][j][k]=\begin{cases} \textit{dp}[i - 1][j][k], & j<\textit{zeros} ~~ | ~~ k<\textit{ones} \\ \max(\textit{dp}[i - 1][j][k], \textit{dp}[i - 1][j - \textit{zeros}][k - \textit{ones}] + 1), & j \ge \textit{zeros} ~ \& ~ k \ge \textit{ones} \end{cases} \]

最终得到 \(\textit{dp}[l][m][n]\) 的值即为答案。

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        int len = strs.size();
        vector<vector<int>> f(m + 1, vector<int>(n + 1));
        for(int i = 0; i < len; i++)
        {
            int cnt0 = 0, cnt1 = 0;
            string str = strs[i];
            for(auto& t : str)
                if(t == '0') cnt0++;
                else cnt1++;

            for(int j = m; j >= cnt0; j--)
                for(int k = n; k >= cnt1; k--)
                    f[j][k] = max(f[j][k], f[j - cnt0][k - cnt1] + 1);
        }
        return f[m][n];
    }
};
posted @ 2021-06-27 10:26  Dazzling!  阅读(30)  评论(0编辑  收藏  举报