Leetcode---879 盈利计划+1049. 最后一块石头的重量 II+474. 一和零 动态规划背包问题专场

879 盈利计划

在这里插入图片描述

思路

三维背包问题。三个维度分别表示几份、容量限制1、容量限制2。
状态转移方程为
{ d p [ i ] [ j ] [ k ] = d p [ i − 1 ] [ j ] [ k ] , j < g r o u p [ i − 1 ] d p [ i ] [ j ] [ k ] = ( d p [ i − 1 ] [ j ] [ k ] + d p [ i − 1 ] [ j − g r o u p [ i − 1 ] ] [ M a t h . m a x ( 0 , k − p r o f i t [ i − 1 ] ) ] ) % m o d , o t h e r s \left \{ \begin{aligned} &dp[i][j][k] = dp[i-1][j][k],&j<group[i-1] \\ &dp [i][j][k] = (dp[i-1][j][k] + dp[i-1][j-group[i-1]][Math.max(0,k-profit[i-1])]) \% mod, &others \end{aligned} \right. {dp[i][j][k]=dp[i1][j][k],dp[i][j][k]=(dp[i1][j][k]+dp[i1][jgroup[i1]][Math.max(0,kprofit[i1])])%mod,j<group[i1]others

因此得到如下代码,详细解释见代码注释。

代码

public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
        int len = group.length;
        int mod = (int)1e9+7;
        //三维分别表示可供选择的工作数量(可选择,也就是对于某些工作可做可不做)、投入多少人(不一定全部用光)、至少可以实现多少利润(大于等于0)
         int[] [] [] dp = new int[len+1][n+1][minProfit+1];
         //处理边界条件
         dp[0][0][0] = 1;

         for (int i=1;i<=len;i++){
             for (int j = 0;j<=n;j++) {
                 for (int k=0;k<=minProfit;k++) {
                     if (j<group[i-1])//如果当前的人数不能做第i份工作,则方案数等于上一份工作的方案数
                         dp[i][j][k] = dp[i-1][j][k];
                     else//如果可以做第i份工作,则当前工作的方案数等于上一份工作的方案数+上一份工作中满足人数不超过j-group[i-1](表示多份工作可以一起做)、且利润不少于0的方案数量(利润等于0表示只做了当前工作,大于0表示除了当前工作还做了其他工作)
                        dp[i][j][k] = (dp[i-1][j][k] + dp[i-1][j-group[i-1]][Math.max(0,k-profit[i-1])]) % mod;
                 }
             }

         }

         int sum = 0;
         //统计满足条件的方案数量即在len份工作中,利润不少于miniProfit的方案数量
         for (int j=0;j<=n;j++){
             sum = (sum+dp[len][j][minProfit])%mod;
        }
         return sum;
    }

1049. 最后一块石头的重量 II

在这里插入图片描述

思路

可以化简为二维背包问题。两个维度分别表示几块、容量限制。详细解释见代码注释。

代码

public int lastStoneWeightII(int[] stones) {
        int len = stones.length;
        //借助目标和的思想,因为要求能达到的剩余石块的最小重量,所以可以把石头分为两堆,使用一堆的总重量减去另一堆的重量即为剩余的重量,
        //这里把两个堆设为A堆和B堆,其中A堆的重量大于等于B堆的重量
        //为了得到最小的剩余石头重量,两个堆的总重量应该尽可能的接近,最佳情况是相等。
        //此处首先获得最佳的情况(因为石料不可分割,所以这个值可能达不到,但这个是最佳情况)
        int sum = 0;
        for (int i: stones)
            sum += i;
        int t = sum / 2;

        //定义数组,第一个元素指的是参与计算的石块数量,第二个元素指的是当前条件下B堆可以达到的最大重量,如果可以达到,则是true,否则是false
        boolean [][] dp = new boolean[len+1][t+1];
        dp[0][0] = true;


        //从1块石头参与计算开始进行动态规划
        for (int i=1;i<=len;i++)
        {
            for (int j=0;j<=t;j++)
            {
                //如果当前B堆的最大重量大于当前石头的重量,存在两种情况-选或不选,选的话-dp[i-1][j-stones[i-1],不选-dp[i-1][j]
                //因为此时判断的是能不能达到j,所以两种情况有一个可以达到即为可以达到
                if (j>=stones[i-1])
                    dp[i][j] = dp[i-1][j] || dp[i-1][j-stones[i-1]];
                else
                    dp[i][j] = dp[i-1][j];
            }
        }

//        for (int i = 0;i<=len;i++)
//        {
//            for (int j=0;j<=t;j++)
//                System.out.print("\t"+dp[i][j] + " ");
//            System.out.println();
//        }
        
        //因为最后求的是B堆可以达到的最大重量,所以从理想情况开始向下遍历,寻找满足条件的最大值
        for (int j=t;j>=0;j--)
        {
            if (dp[len][j])
                return sum-2*j;
        }
        return 0;
    }

474. 一和零

在这里插入图片描述

思路

同样是三维背包问题。三个维度分别表示几份、容量限制1、容量限制2。
状态转移方程为
{ d p [ i ] [ j ] [ k ] = d p [ i − 1 ] [ j ] [ k ] , o t h e r s d p [ i ] [ j ] [ k ] = M a t h . m a x ( d p [ i ] [ j ] [ k ] , d p [ i − 1 ] [ j − c [ 0 ] ] [ k − c [ 1 ] ] ) , j < = c [ 0 ] & & k < = c [ 1 ] \left \{ \begin{aligned} &dp[i][j][k] = dp[i-1][j][k],&others \\ &dp [i][j][k] =Math.max (dp[i][j][k] ,dp[i-1][j-c[0]][k-c[1]]), &j<=c[0] \&\& k<=c[1]\end{aligned} \right. {dp[i][j][k]=dp[i1][j][k],dp[i][j][k]=Math.max(dp[i][j][k],dp[i1][jc[0]][kc[1]]),othersj<=c[0]&&k<=c[1]

因此得到如下代码。

代码

public int findMaxForm(String[] strs, int m, int n) {
        int len = strs.length;

        //最后判断满足条件的最大元素个数
        int[][][] dp = new int[len+1][m+1][n+1];

        for (int i=1;i<=len;i++)
        {
            int[] c = count_num(strs[i-1]);
            for (int j=0;j<=m;j++)
            {
                for (int k=0;k<=n;k++)
                {
                    dp[i][j][k] = dp[i-1][j][k];
                    if (c[0]<=j && c[1]<=k)
                        dp[i][j][k] = Math.max(dp[i][j][k], dp[i-1][j-c[0]][k-c[1]]+1);

                }
            }
        }
        return dp[len][m][n];
    }

    public int[] count_num(String str)
    {
        int[] a = new int[2];
        Arrays.fill(a,0);

        for (char ch : str.toCharArray())
        {
            if (ch == '1')
                a[1]++;
            else
                a[0]++;
        }
        return a;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值