题目
大意:给出一个全为01串的字符串数组,每个串都会消耗一定数量的0和1,分别给出0和1的总个数,要求在消耗完给出的01资源之前找出最多的字符串。
Example 1:
Input: Array = {"10", "0001", "111001", "1", "0"}, m = 5, n = 3
Output: 4
Explanation: This are totally 4 strings can be formed by the using
of 5 0s and 3 1s, which are “10,”0001”,”1”,”0”
Example 2:
Input: Array = {"10", "0", "1"}, m = 1, n = 1
Output: 2
Explanation: You could form "10", but then you'd have nothing
left. Better form "0" and "1".
思路
把模型转换一下,就发现这是一个01背包问题。
了解背包问题
将该题类比为01背包
物品:每个字符串
物品的重量:该字符串消耗的0和1的数量
物品的价值:1,用于计数,要求最大总价值即选出字符串的数量最多
背包总容量:[m][n],m为给定 0 的个数,n为给定 1 的个数
特点:每个物品(字符串)只能取一次,因此是01背包;而且不要求恰好用完01的资源,即背包不需要恰好装满,可以不装满。
定义状态
dp[i][zero][one]:在背包容量为[zero][one]时,从前i个字符串选出若干串放进背包,所获得的最大字符串数量。
因为背包不要求恰好装满(0和1不一定要恰好消耗完),所以初始值全为0。
状态转换
字符串str消耗的0和1为:n0, n1
若n0 > zero 或者 n1 > one,则str不可能放进当前背包
dp[i + 1][zero][one] = dp[i][zero][one]
否则,str有机会放进当前背包,需要与不放进该背包的情况比较,取最大值
dp[i + 1][zero][one] = max(1 + dp[i][zero - n0][one - n1], dp[i][zero][one] )
最终答案为
dp[size of strings][m][n]
优化空间
可以把三维降为二维,因为新的二维数组的数值都依赖于前一个二维数组,注意二维数组中的元素从大往小更新。
dp[zero][one] = max(1 + dp[zero - n0][one - n1], dp[zero][one])
最终答案为
dp[m][n]
代码
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
int size = strs.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));//二维数组代替三维,节省空间
for (int i = 0; i < size; i++) {
//计算每个字符串消耗的0和1
int n0 = 0, n1 = 0;
for (int j = 0; j < strs[i].length(); j++) if (strs[i][j] == '0') n0++;
n1 = strs[i].length() - n0;
//从大往小计算,避免右边找不到其所依赖的上一轮左边的值,若从左到右会过早覆盖旧值
for (int zero = m; zero >= n0; zero--) {
for (int one = n; one >= n1; one--) {
dp[zero][one] = max(dp[zero][one], 1 + dp[zero - n0][one - n1]);
}
}
}
return dp[m][n];
}
};