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)