动态规划|474. 一和零

题目看上去很唬人,但是恰恰是这样说明该题设计的目的很强,指向dp的01背包,就是为了考01背包设计的。

像极了中学时代的那种看上去花里胡哨,实质上是根据考点设计出题的题目。(这种题看破出题意图,往往都很简单)

为什么说是考察01背包,代码随想录–474.一和零中描述的很清楚,不赘述。

这里直接给出转移方程:
f ( k , i , j ) = f ( k − 1 , i , j ) + ( k − 1 , i − [ k ] , j − [ k ] ) f(k,i,j)=f(k-1,i,j)+(k-1,i-[k],j-[k]) f(k,i,j)=f(k1,i,j)+(k1,i[k],j[k])
其中:k表示候选元素的范围,(i,j)表示0与1的上限个数。


既然有了转移方程,那么该如何遍历呢?
就如同01背包一样,先循环k或者先循环i,j都是可以的,不过在力扣上先循环k的速度要快些,先循环i,j速度要比先k慢上不少;
这是先循环k的性能;
在这里插入图片描述
这是先循环i,j的性能;
在这里插入图片描述

k先循环的Java代码如下:

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int len = strs.length;
        int[][][] f = new int[len+1][m+1][n+1];
        
        for(int k = 1; k <= len; k++){//先循环k
            String s = strs[k-1];
            int[] set = getSet(s);
            for(int i = 0; i <= m; i++){
                for(int j = 0; j <= n; j++){
                    f[k][i][j] = f[k-1][i][j];
                    if(i-set[0] >= 0 && j-set[1] >= 0)
                    f[k][i][j] = Math.max(f[k][i][j], f[k-1][i-set[0]][j-set[1]] + 1);
                }            
            }
        }
        return f[len][m][n];
    }
    int[] getSet(String s){
        int[] set = new int[2];
        for(int i = 0; i < s.length(); i++){
            if(s.charAt(i)-'0' == 0) set[0]++;
            else set[1]++;
        }
        return set;
    }
}

先循环 i , j i,j i,j的代码如下:

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int len = strs.length;
        int[][][] f = new int[len+1][m+1][n+1];
        
        for(int i = 0; i <= m; i++){//先循环i,j
            for(int j = 0; j <= n; j++){
                for(int k = 1; k <= len; k++){
                    String s = strs[k-1];
                    int[] set = getSet(s);
                    f[k][i][j] = f[k-1][i][j];
                    if(i-set[0] >= 0 && j-set[1] >= 0)
                    f[k][i][j] = Math.max(f[k][i][j], f[k-1][i-set[0]][j-set[1]] + 1);
                }            
            }
        }
        return f[len][m][n];
    }
    int[] getSet(String s){
        int[] set = new int[2];
        for(int i = 0; i < s.length(); i++){
            if(s.charAt(i)-'0' == 0) set[0]++;
            else set[1]++;
        }
        return set;
    }
}

既然是01背包,那么肯定可以通过滚动数组的方式将三维优化至二维,减少空间开销(方法核心与01背包空间优化一致),Java代码如下:

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int len = strs.length;
        int[][] f = new int[m+1][n+1];
        
        for(int k = 1; k <= len; k++){//先循环k
            String s = strs[k-1];
            int[] set = getSet(s);
            for(int i = m; i >= set[0]; i--){//核心点在于i,j的遍历上下界及顺序
                for(int j = n; j >= set[1]; j--){
                    f[i][j] = f[i][j];
                    if(i-set[0] >= 0 && j-set[1] >= 0)
                    f[i][j] = Math.max(f[i][j], f[i-set[0]][j-set[1]] + 1);
                }            
            }
        }
        return f[m][n];
    }
    int[] getSet(String s){
        int[] set = new int[2];
        for(int i = 0; i < s.length(); i++){
            if(s.charAt(i)-'0' == 0) set[0]++;
            else set[1]++;
        }
        return set;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值