20210705力扣第474题:一和零(java)

题目

给你一个二进制字符串数组 strs 和两个整数 m 和 n 。

请你找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。

示例 1:

输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5031 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"}{"10","1","0"}{"111001"} 不满足题意,因为它含 41 ,大于 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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ones-and-zeroes
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


想法

不得不说,遇到这道题,又是不会做,怎么这么难嘞。
还是那五步骤:

1.dp数组的定义和下标。

2.递推公式。

3.dp数组如何初始化,初始化也需要注意。

4.遍历顺序,比较考究,01 先遍历背包,后遍历物品。

4.1排列和组合的遍历顺序是不相同的。

4.1.1 排列:背包在外 物品在内。

4.1.2 组合:物品在外,背包在内。

5.(出现问题)打印dp数组。(打印dp数组,检查是否有问题,检验1 2 3 4 步骤)

这道题我也不会,就看了评论,评论和官方解答有用二维的,也有用三维的,三维的居多,我不会,所以先看了二维的。二维的也按照上面的顺序分析一下什么含义。

  1. dp[i][j]就是有i个0和j个1时候的最大子集。
  2. dp[i][j] = Math.max(dp[i][j],dp[i-zeronum][j-onenum]+1);本身或者减去所消耗的0和1的次数。
  3. 初始化 那就是dp[0][0]=0;
  4. 考察的是集合,相当于组合,那应该是物品在外,背包在内。

大致想法,先循环物品,记录下0和1的个数, 再循环背包,然后遍历每个字符串中所含有的0 和1的个数,进行递推公式。

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
	int[][] dp = new int[m+1][n+1];
	dp[0][0] = 0 ; 
	//遍历 物品 string
	for(String str:strs)
	{
		//记录每个str的0和1的个数
		int zeronum = 0,onenum = 0;
		for(char c:str.toCharArray())
		{
			if(c=='0')  zeronum++;
			else onenum++;
		}

	//开始进行动态方程
	//这里动态方程自上而下  数组初始化时候都是0
	for(int i = m ; i >=zeronum;i--)
	{
		for(int j = n ;  j >=onenum;j--)
		{
			dp[i][j] = Math.max(dp[i][j],1+dp[i-zeronum][j-onenum]);
		}
	}
	
	}
	return dp[m][n];
	
	}
}

在写的时候,要想到之前学动态规划时候的一张数组表,这个数组表印在脑子里,这样才能知道动态规划的递归方程怎么来的。我根据评论中的构建一个二维的数组表,开始的时候初始化其实都是dp[i][j] =0;但是循环一次 就会+1,最后,就成为最大子集。
其实吧,这两个for循环那里 我还是不太好懂。自己画了一个二维表,第一次的时候,大部分都是0+1等于1,横坐标代表0的个数,当你0消耗了,那就减去0的个数,1消耗了就减去1的个数。

官方答案 三维数组

通常背包就只有一种容量,但是这道题有两个容量 分别是0 和1;二维的时候是物品和容量,三维的是字符串,0容量1 容量。那就需要定义三维数组d[i][j][k],表示在第i个个字符串中,j个0和k个1的字符串数量。

初始化应该是dp[0][0][0]=0;
边界应该是 l m n ,l是字符串的长度。

当j k 小于0 和1 的数量 不选第i个字符串的时候就是dp[i-1][j][k];

当j k 大于0 1 的 数量 不选第i个字符串的时候就是dp[i-1][j][k];
如果选的话,就是dp[i][j-zeronum][k-onenum];
还有在注意一下for循环的顺序就好。
接下来写一下代码:

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int[][][] dp = new int[strs.length+1][m+1][n+1];
        dp[0][0][0] = 0 ;

        for(int i = 1 ;i<=strs.length;i++)
        {
            int zeronum = 0 ,onenum = 0 ;
            for(char c: strs[i-1].toCharArray())
            {
                if(c == '0') zeronum++;
                else  onenum++;
            }
            for(int j = 0 ; j<=m;j++)
            {
                for(int k = 0; k<=n;k++)
                {
                    dp[i][j][k] = dp[i-1][j][k];
                    if(j>=zeronum&&k>=onenum)
                    {
                        dp[i][j][k] = Math.max(dp[i-1][j][k],dp[i-1][j-zeronum][k-onenum]+1);
                    }
                }
            }
        }
        return dp[strs.length][m][n];
	
	}
}

总结

写完二维的再去写三维的,忽然感觉自己够写出代码了,其实嗄,在面对01背包问题,经常回去看答案,现在要能培养出自己不看答案独立思考的能力才行啊,从那五步入手,dp[i][j]的定义,初始化,for循环的内外顺序,动态方程怎么写,每次做题时候这些需要考虑到,在本题当中,那个动态方程需要进行判断,如果想不到,那也做不出来,还有就是考虑边界问题等等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目描述: 给你两个版本号 version1 和 version2 ,请你比较它们。 版本号由一个或多个修订号组成,各修订号由一个 '.' 连接。每个修订号由多数字组成,可能包含前导。每个版本号至少包含一个字符。修订号从左到右编号,下标从0开始,最左边的修订号下标为0 ,下一个修订号下标为1,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。 比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较忽略任何前导后的整数值。也就是说,修订号1和修订号001相等。如果版本号没有指定某个下标处的修订号,则该修订号视为0。例如,版本1.0 小于版本1.1,因为它们下标为0的修订号相同,而下标为1的修订号分别为0和1,0 < 1。 返回规则如下: 如果 version1 > version2 返回 1, 如果 version1 < version2 返回 -1, 否则返回 0。 示例 1: 输入:version1 = "1.01", version2 = "1.001" 输出:0 解释:忽略前导,"01" 和 "001" 都表示相同的整数 "1" 示例 2: 输入:version1 = "1.0", version2 = "1.0.0" 输出:0 解释:version1 没有指定下标为 2 的修订号,即视为 "0" 示例 3: 输入:version1 = "0.1", version2 = "1.1" 输出:-1 解释:version1 下标为 0 的修订号是 0,version2 下标为 0 的修订号是 1 。0 < 1,所以 version1 < version2 示例 4: 输入:version1 = "1.0.1", version2 = "1" 输出:1 示例 5: 输入:version1 = "7.5.2.4", version2 = "7.5.3" 输出:-1 提示: 1 <= version1.length, version2.length <= 500 version1 和 version2 仅包含数字和 '.' version1 和 version2 都是 有效版本号

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值