一和零(动态规划+多维背包问题)
题目
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
示例 1:
输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它
含 4 个 1 ,大于 n 的值 3 。
示例 2:输入:strs = ["10", "0", "1"], m = 1, n = 1
输出:2
解释:最大的子集是 {"0", "1"} ,所以答案是 2 。提示:
1 <= strs.length <= 600
1 <= strs[i].length <= 100
strs[i] 仅由 '0' 和 '1' 组成
1 <= m, n <= 100
解题思路
第一次碰到这样的题,今天一口气刷了两道,两道都是动态规划问题,并且本题是多维的背包0/1问题。多维背包问题和一维背包问题很像,但是有时可以进行空间上的优化。不太明白背包问题的可以看下这个视频bilili
对于背包问题,最重要的就是得到背包的状态转移方程,对于本题状态方程应该为:
现有 X 种物品,每种物品体积为 Wx ,价值为 Vx
ans[x][j + wx][k + wx] = max(ans[x - 1][i + wx][k + wx], ans[x - 1][i][k] + vx)
当背包放得下时用上面的状态方程进行背包状态的更新
/*放得下但不放*/
ans[x - 1][i + wx][k + wx]
/*移除体积后,放入*/
ans[x - 1][i][k] + vx
对于本题,可以维护一个三维数组来记录状态。
int ans[strsSize + 1][m + 1][n + 1];
第一维表示添加第i个字符串,m,n分别表示0,1的剩余空间。
三重循环更新背包状态。
for(int j = 0; j <= m; ++j){
for(int k = 0; k <= n; ++k){
if(j >= mc && k >= nc){//放得下,看放不放
ans[i][j][k] = max(ans[i - 1][j][k], ans[i - 1][j - mc][k - nc] + 1);
}else{//放不下
ans[i][j][k] = ans[i - 1][j][k];
}
}
}
完整代码如下:
void get_count(char *str, int *mc, int *nc){
int m = 0, n = 0;
for(int i = 0; str[i]; i++){
if(str[i] == '0'){
m++;
}
if(str[i] == '1'){
n++;
}
}
*mc = m;
*nc = n;
}
int max(int a, int b){
return a > b ? a : b;
}
int findMaxForm(char ** strs, int strsSize, int m, int n){
int ans[strsSize + 1][m + 1][n + 1];
memset(ans, 0, sizeof(ans));
for(int i = 1; i <= strsSize; i++){
int mc, nc;
get_count(strs[i - 1], &mc, &nc);
for(int j = 0; j <= m; ++j){
for(int k = 0; k <= n; ++k){
if(j >= mc && k >= nc){//放得下,看放不放
ans[i][j][k] = max(ans[i - 1][j][k], ans[i - 1][j - mc][k - nc] + 1);
}else{
ans[i][j][k] = ans[i - 1][j][k];
}
}
}
}
return ans[strsSize][m][n];
}

浙公网安备 33010602011771号