LeetCode 879. 盈利计划(动规典范题!)/ 牛客:找零 / 机器人跳跃问题

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 879. 盈利计划(动规典范题!)/ 牛客:找零 / 机器人跳跃问题相关的知识,希望对你有一定的参考价值。

879. 盈利计划

2021.6.9每日一题

题目描述

集团里有 n 名员工,他们可以完成各种各样的工作创造利润。

第 i 种工作会产生 profit[i] 的利润,它要求 group[i] 名成员共同参与。如果成员参与了其中一项工作,就不能参与另一项工作。

工作的任何至少产生 minProfit 利润的子集称为 盈利计划 。并且工作的成员总数最多为 n 。

有多少种计划可以选择?因为答案很大,所以 返回结果模 10^9 + 7 的值。


示例 1:

输入:n = 5, minProfit = 3, group = [2,2], profit = [2,3]
输出:2
解释:至少产生 3 的利润,该集团可以完成工作 0 和工作 1 ,或仅完成工作 1 。
总的来说,有两种计划。
示例 2:

输入:n = 10, minProfit = 5, group = [2,3,5], profit = [6,7,8]
输出:7
解释:至少产生 5 的利润,只要完成其中一种工作就行,所以该集团可以完成任何工作。
有 7 种可能的计划:(0),(1),(2),(0,1),(0,2),(1,2),以及 (0,1,2) 。

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

思路

(做完,这个题真的很好!!!!以后再来看)
正常dp思路,超出内存限制了,
dp[i][j][k]定义为在前i个工作中,有j个员工参与工作,并且当前利润为k的情况数目
看了一下,主要是维度是三维,而且后面两个维度最大都是10000,乘起来就太大了

class Solution {
    public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
        //首先还是想到暴力解,就是遍历所有利润大于minProfit的组合,然后看需要的人数是否小于等于n
        //怎么动态规划呢,想想
        //dp的第一维,就是index,第二维人数,第三维利润,然后目标就是选择数目
        //当前工作i,利润p[i],人数g[i]
        //dp[i][j][k] = dp[i - 1][j][k] + dp[i - 1][j - g[i]][k - p[i]] 

        //试着写一下吧
        int l = profit.length;
        int mansum = 0; 
        int prosum = 0;
        for(int i = 0; i < l; i++){
            mansum += group[i];
            prosum += profit[i];
        }
        if(minProfit > prosum)
            return 0;
        int[][][] dp = new int[l + 1][mansum + 1][prosum + 1];

        //初始化,没有工作,0个人,0利润
        //0个工人和0利润的时候,没有意义,也就是0,默认初始化为0
        dp[0][0][0] = 1;
        for(int i = 1; i <= l; i++){
            for(int j = 0; j <= mansum; j++){
                for(int k = 0; k <= prosum; k++){
                    dp[i][j][k] = dp[i - 1][j][k];
                    if(j >= group[i - 1] && k >= profit[i - 1])
                        dp[i][j][k] += dp[i - 1][j - group[i - 1]][k - profit[i - 1]];
                }
            }
        }
        int res = 0;
        int t = n < mansum ? n : mansum;
        for(int i = 0; i <= t; i++){
            for(int j = minProfit; j <= prosum; j++){
                res += dp[l][i][j];
            }
        }
        return res;
    }   
}

然后就想着是否人数可以固定在n,因为大于n其实也没什么意义,所以先对第二维进行了优化
然后又在第40个示例上超内存了,然后又想优化第三维了,直接改第三维为minprofit结果错了
这里第三维定义为利润为k,就不能直接改成minprofit了,这里需要将第三维的定义为利润至少为k

在这种定义下,当k大于当前利润的时候,只需要完成这个工作就可以达到k的要求,所以需要从 dp[i][j][0]转移而来

class Solution {
    public static final int MOD = (int)1e9 + 7;
    public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
        //改了dp定义
        int l = profit.length;
        
        int[][][] dp = new int[l + 1][n + 1][minProfit + 1];

        //初始化,没有工作,0个人,0利润
        //0个工人和0利润的时候,没有意义,也就是0,默认初始化为0
        dp[0][0][0] = 1;
        for(int i = 1; i <= l; i++){
            for(int j = 0; j <= n; j++){
                for(int k = 0; k <= minProfit; k++){
                    dp[i][j][k] = dp[i - 1][j][k];
                    //因为如果k取值最大为minProfit的话,比minProfit大的值,要取0才对
                    if(j >= group[i - 1] && k >= profit[i - 1])
                        dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j - group[i - 1]][k - profit[i - 1]]) % MOD;
                    else if(j >= group[i - 1] && k < profit[i - 1])
                        dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j - group[i - 1]][0]) % MOD;
                }
            }
        }
        int res = 0;
        for(int i = 0; i <= n; i++){
            res = (res + dp[l][i][minProfit]) % MOD;
        }
        return res;
    }   
}

状态压缩,这里看官解又发现个问题,就是对于第二维的定义,到底是当前恰好有j个人参与工作,还是最多j个人参与工作,
如果状态定义为当前j个人参与工作,最后结果就需要0~n累加;如果状态定义为最多j个人参与工作,那么初始化的时候,j > 0也应该初始化为1,因为包括了j=0的情况,而j=0时,dp[0][0]是1,最后结果就直接输出dp[n][profit]就可以了
当然,上面三维的状态定义也可以定义为最多有j个人参与工作,相应的初始化,也要发生变化,即dp[0][j][0]= 1

class Solution {
    public static final int MOD = (int)1e9 + 7;
    public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
        //状态压缩,第二维定义为当前恰好有j个人参与工作
        
        int l = profit.length;
        
        int[][] dp = new int[n + 1][minProfit + 1];

        //初始化,没有工作,0个人,0利润
        //0个工人和0利润的时候,没有意义,也就是0,默认初始化为0
        //for(int i = 0; i <= n; i++){
        //   dp[i][0] = 1;
        //}
        dp[0][0] = 1;
        for(int i = 1; i <= l; i++){
            for(int j = n; j >= 0; j--){
                for(int k = minProfit; k >= 0; k--){
                    //因为如果k取值最大为minProfit的话,比minProfit大的值,要取0才对
                    if(j >= group[i - 1] && k >= profit[i - 1])
                        dp[j][k] = (dp[j][k] + dp[j - group[i - 1]][k - profit[i - 1]]) % MOD;
                    else if(j >= group[i - 1] && k < profit[i - 1])
                        dp[j][k] = (dp[j][k] + dp[j - group[i - 1]][0]) % MOD;
                }
            }
        }
        int res = 0;
        for(int i = 0; i <= n; i++){
            res = (res + dp[i][minProfit]) % MOD;
        }
        return res;
    }   
}

状态压缩,第二维定义为当前最多有j个人参与工作

class Solution {
    public static final int MOD = (int)1e9 + 7;
    public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
        //状态压缩,第二维定义为当前最多有j个人参与工作
        
        int l = profit.length;
        
        int[][] dp = new int[n + 1][minProfit + 1];

        //初始化,没有工作,0个人,0利润
        //0个工人和0利润的时候,没有意义,也就是0,默认初始化为0
        for(int i = 0; i <= n; i++){
           dp[i][0] = 1;
        }
        for(int i = 1; i <= l; i++){
            for(int j = n; j >= 0; j--){
                for(int k = minProfit; k >= 0; k--){
                    //因为如果k取值最大为minProfit的话,比minProfit大的值,要取0才对
                    if(j >= group[i - 1] && k >= profit[i - 1])
                        dp[j][k] = (dp[j][k] + dp[j - group[i - 1]][k - profit[i - 1]]) % MOD;
                    else if(j >= group[i - 1] && k < profit[i - 1])
                        dp[j][k] = (dp[j][k] + dp[j - group[i - 1]][0]) % MOD;
                }
            }
        }

        return dp[n][minprofit];
    }   
}

三维,第二维定义为当前最多有j个人参与工作

class Solution {
    public static final int MOD = (int)1e9 + 7;
    public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
        //首先还是想到暴力解,就是遍历所有利润大于minProfit的组合,然后看需要的人数是否小于等于n
        //怎么动态规划呢,想想
        //dp的第一维,就是index,第二维人数,第三维利润,然后目标就是选择数目
        //当前工作i,利润p[i],人数g[i]
        //dp[i][j][k] = dp[i - 1][j][k] + dp[i - 1][j - g[i]][k - p[i]] 

        //试着写一下吧
        int l = profit.length;
        
        int[][][] dp = new int[l + 1][n + 1][minProfit + 1];

        //初始化,没有工作,0个人,0利润
        //0个工人和0利润的时候,没有意义,也就是0,默认初始化为0
 
        for(int i = 0; i <= n; i++){
            dp[0][i][0] = 1;
        }
        for(int i = 1; i <= l; i++){
            for(int j = 0; j <= n; j++){
                for(int k = 0; k <= minProfit; k++){
                    dp[i][j][k] = dp[i - 1][j][k];
                    //因为如果k取值最大为minProfit的话,比minProfit大的值,要取0才对
                    if(j >= group[i - 1] && k >= profit[i - 1])
                        dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j - group[i - 1]][k - profit[i - 1]]) % MOD;
                    else if(j >= group[i - 1] && k < profit[i - 1])
                        dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j - group[i - 1]][0]) % MOD;
                }
            }
        }

        return dp[l][n][minProfit];
    }   
}

找零

题目描述

链接:https://www.nowcoder.com/questionTerminal/944e5ca0ea88471fbfa73061ebe95728
来源:牛客网

Z国的货币系统包含面值1元、4元、16元、64元共计4种硬币,以及面值1024元的纸币。
现在小Y使用1024元的纸币购买了一件价值为N (0 < N \\le 1024)N(0<N≤1024)的商品,请问最少他会收到多少硬币?

输入描述:
一行,包含一个数N。


输出描述:
一行,包含一个数,表示最少收到的硬币数。

示例1
输入
200
输出
17
说明
花200,需要找零824块,找12个64元硬币,3个16元硬币,2个4元硬币即可。

思路

简单的贪心,秒了

import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int extra = 1024 - n;
        int res = extra / 64;
        extra = extra % 64;
        res += extra / 16;
        extra = extra % 16;
        res += extra / 4;
        extra = extra % 4;
        res += extra / 1;
        System.out.println(res);
        
    }
}

机器人跳跃问题

链接:https://www.nowcoder.com/questionTerminal/7037a3d57bbd4336856b8e16a9cafd71
来源:牛客网

机器人正在玩一个古老的基于DOS的游戏。游戏中有N+1座建筑——从0到N编号,从左到右排列。编号为0的建筑高度为0个单位,编号为i的建筑的高度为H(i)个单位。 

起初, 机器人在编号为0的建筑处。每一步,它跳到下一个(右边)建筑。假设机器人在第k个建筑,且它现在的能量值是E, 下一步它将跳到第个k+1建筑。它将会得到或者失去正比于与H(k+1)与E之差的能量。如果 H(k+1) > E 那么机器人就失去 H(k+1) - E 的能量值,否则它将得到 E - H(k+1) 的能量值。

游戏目标是到达第个N建筑,在这个过程中,能量值不能为负数个单位。现在的问题是机器人以多少能量值开始游戏,才可以保证成功完成游戏?

输入描述:
第一行输入,表示一共有 N 组数据.

第二个是 N 个空格分隔的整数,H1, H2, H3, ..., Hn 代表建筑物的高度


输出描述:
输出一个单独的数表示完成游戏所需的最少单位的初始能量

示例1
输入
5
3 4 3 2 4
输出
4

示例2
输入
3
4 4 4
输出
4

示例3
输入
3
1 6 4
输出
3

备注:
数据约束:
1 <= N <= 10^5
1 <= H(i) <= 10^5

思路

弄清楚题意,能否跳出一步,和两个楼的高度差没有关系,只和当前能量和下一个楼的高度有关系,这样的话从后往前推导就可以形成这个关系式

//看了一下,从后向前盘这个逻辑好像就行了
import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] H = new int[n + 1];
        for(int i = 1; i <= n; i++){
            H[i] = sc.nextInt();
        }
        //刚刚想错了,刚刚想成和两个楼的高度有关了,应该是按能量来推
        //当前能量
        int energy = 0;
        for(int i = nLeetCode 879. 盈利计划(dp)

LeetCode 879 盈利计划[动态规划] HERODING的LeetCode之路

879. 盈利计划(多维背包问题)

879. 盈利计划(多维背包问题)

879. 盈利计划

879. 盈利计划