20210609 每日一题 盈利计划

题目

题目链接

代码

class Solution {
public:
    int profitableSchemes(int G, int P, vector<int>& group, vector<int>& profit) {

    }
};

方法一:三维动态规划

分析

该题目是一道典型的多维背包问题,设定三维数组 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] 表示在前 i i i 个工作中,使用 j j j 个工人,工作获利至少为 k k k 的盈利计划的总数目。对于第 i i i 个工作有选和不选两种情况:

  • 如果 j ≥ g r o u p [ i ] j \geq group[i] jgroup[i] 时,当前小组人数为 g r o u p [ i ] group[i] group[i],工作获利为 p r o f i t [ i ] profit[i] profit[i],则有工作获利至少为 k k k 的盈利计划的总数目为前 i − 1 i-1 i1 个工作中,使用 j j j 个工人,工作获利至少为 k k k 的盈利计划的总数目加上在前 i − 1 i-1 i1 个工作中,使用 j − g r o u p [ i ] j- group[i] jgroup[i] 个工人,工作获利至少为 k − p r o f i t [ i ] k - profit[i] kprofit[i] 的盈利计划的总数目,值得注意的是工作获利不可能低于 0 0 0,因此盈利计划的总数目为: d p [ i ] [ j ] [ k ] = d p [ i − 1 ] [ j ] [ k ] + d p [ i − 1 ] [ j − g r o u p [ i ] ] [ max ⁡ ( 0 , k − p r o f i t [ i ] ) ] dp[i][j][k] = dp[i - 1][j][k] + dp[i - 1][j - group[i]][\max(0, k - profit[i])] dp[i][j][k]=dp[i1][j][k]+dp[i1][jgroup[i]][max(0,kprofit[i])],即
  • 如果 j < g r o u p [ i ] j < group[i] j<group[i] 时,则不能选 g r o u p [ i ] group[i] group[i],则盈利计划的总数目为: d p [ i ] [ j ] [ k ] = d p [ i − 1 ] [ j ] [ k ] dp[i][j][k]=dp[i-1][j][k] dp[i][j][k]=dp[i1][j][k]

综上所述 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] 的状态转移方程可表示为:
d p [ i ] [ j ] [ k ] = { d p [ i ] [ j ] [ k ] = d p [ i − 1 ] [ j ] [ k ] + d p [ i − 1 ] [ j − g r o u p [ i ] ] [ max ⁡ ( 0 , k − p r o f i t [ i ] ) i f   j ≥ g r o u p [ i ] d p [ i − 1 ] [ j ] [ k ] i f   j < g r o u p [ i ] dp[i][j][k] = \begin{cases} dp[i][j][k] = dp[i - 1][j][k] + dp[i - 1][j - group[i]][\max(0, k - profit[i]) &if\ j \geq group[i]\\ dp[i-1][j][k] &if\ j < group[i] \end{cases} dp[i][j][k]={dp[i][j][k]=dp[i1][j][k]+dp[i1][jgroup[i]][max(0,kprofit[i])dp[i1][j][k]if jgroup[i]if j<group[i] 问题答案为:
∑ i = 0 n dp [ len ] [ i ] [ minProfit ] \sum_{i=0}^{n}\textit{dp}[\textit{len}][i][\textit{minProfit}] i=0ndp[len][i][minProfit]

代码

class Solution {
public:
    int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
        int len = group.size();
        vector<vector<vector<int>>> dp (len + 1, vector<vector<int>>(n + 1, vector<int>(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]) {
                        dp[i][j][k] = (dp[i - 1][j][k] + dp[i - 1][j - group[i - 1]][max(0, k - profit[i - 1])]) % ((int)1e9 + 7);
                    } else {
                        dp[i][j][k] = dp[i - 1][j][k];
                    }
                }
            }
        }
        int sum = 0;
        for (int j = 0; j <= n; j++) {
            sum = (sum + dp[len][j][minProfit]) % ((int)1e9 + 7);
        }
        return sum;
    }
};

复杂度分析

  • 时间复杂度: O ( l e n × n × m i n P r o f i t ) O(len \times n \times minProfit) O(len×n×minProfit),其中 $len $ 是数组长度, n n n 是可使用员工数, m i n P r o f i t minProfit minProfit 是最小利润。
  • 空间复杂度: O ( l e n × n × m i n P r o f i t ) O(len \times n \times minProfit) O(len×n×minProfit),其中 l e n len len 是数组长度, n n n 是可使用员工数, m i n P r o f i t minProfit minProfit 是最小利润,实现动态规划需要创建 l e n × n × m i n P r o f i t len \times n \times minProfit len×n×minProfit 的三维数组 d p dp dp

方法二:二维动态规划

分析

根据方法一的状态转移方程可知,更新 d p [ i ] [ ] [ ] dp[i][][] dp[i][][] 的每个元素值时,只依赖于 d p [ i − 1 ] [ ] [ ] dp[i-1][][] dp[i1][][] 的元素值。因此,可以使用一维滚动数组 d p [ j ] [ k ] dp[j][k] dp[j][k] 来替代二维数组 d p [ i ] [ j ] dp[i][j] dp[i][j] 对方法三进行空间上的优化。 d p [ j ] [ k ] dp[j][k] dp[j][k] 的状态转移方程可表示为:
d p [ j ] [ k ] = d p [ j ] [ k ] + d p [ j − g r o u p [ i ] ] [ max ⁡ ( 0 , k − p r o f i t [ i ] dp[j][k] = dp[j][k]+dp[j - group[i]][\max(0, k - profit[i] dp[j][k]=dp[j][k]+dp[jgroup[i]][max(0,kprofit[i] 问题答案为: ∑ i = 0 n d p [ i ] [ m i n P r o f i t ] \sum_{i=0}^{n} dp[i][minProfit] i=0ndp[i][minProfit]

要点

根据方法一的状态转移方程,实现时内层循环需采用倒序遍历的方式更新元素值。

代码

class Solution {
public:
    int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
        const int MOD = 1e9 + 7;
        vector<vector<int>> dp (n + 1, vector<int>(minProfit + 1));
        dp[0][0] = 1;
        int len = group.size();
        for(int i = 0; i < len; ++i) {
            for(int j = n; j >= group[i]; --j) {
                for(int k = minProfit; k >= 0; --k) {
                    dp[j][k] = (dp[j][k] + dp[j - group[i]][max(0, k - profit[i])]) % MOD;
                }
            }
        }
        int sum = 0;
        for (int j = 0; j <= n; j++) {
            sum = (sum + dp[j][minProfit]) % MOD;
        }
        return sum;
    }
};

复杂度分析

  • 时间复杂度: O ( l e n × n × m i n P r o f i t ) O(len \times n \times minProfit) O(len×n×minProfit),其中 $len $ 是数组长度, n n n 是可使用员工数, m i n P r o f i t minProfit minProfit 是最小利润。
  • 空间复杂度: O ( n × m i n P r o f i t ) O(n \times minProfit) O(n×minProfit),其中 n n n 是可使用员工数, m i n P r o f i t minProfit minProfit 是最小利润,实现动态规划需要创建 n × m i n P r o f i t n \times minProfit n×minProfit 的二维数组 d p dp dp

方法三:转换动态规划

分析

题目要求
最多使用有 n n n 名员工且工作的任何至少产生 m i n P r o f i t minProfit minProfit 利润,因此可以表示为 g ≤ n g \leq n gn p ≥ m i n P r o f i t p \geq minProfit pminProfit g g g为实际所用员工数, p p p为实际盈利。根据容斥原理可以变化为:
N ( g ≤ n ) = N ( g ≤ n ,   p ≥ m i n P r o f i t ) + N ( g ≤ n ,   p < m i n P r o f i t ) N(g \leq n)=N(g \leq n,\ p \geq minProfit)+N(g \leq n,\ p < minProfit) N(gn)=N(gn, pminProfit)+N(gn, p<minProfit) N ( g ≤ n ,   p ≥ m i n P r o f i t ) = N ( g ≤ n ) − N ( g ≤ n ,   p < m i n P r o f i t ) N(g \leq n,\ p \geq minProfit)=N(g \leq n)-N(g \leq n,\ p < minProfit) N(gn, pminProfit)=N(gn)N(gn, p<minProfit)

代码

class Solution {
public:
    int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
        const int MOD = 1e9 + 7;
        int len = group.size();

        vector<vector<int>> dpTwoD(len + 1, vector<int>(n + 1, 0));
        dpTwoD[0][0] = 1;
        for(int i = 1; i <= len; ++i) {
            for(int j = 0; j <= n; ++j) {
                if(j >= group[i - 1]) {
                    dpTwoD[i][j] = (dpTwoD[i - 1][j] + dpTwoD[i - 1][j - group[i - 1]]) % MOD;
                } else {
                    dpTwoD[i][j] = dpTwoD[i - 1][j];
                }
            }
        }
        
        vector<vector<vector<int>>> dpThreeD (len + 1, vector<vector<int>>(n + 1, vector<int>(minProfit, 0)));
        if(minProfit > 0) dpThreeD[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] && k >= profit[i - 1]) {
                        dpThreeD[i][j][k] = (dpThreeD[i - 1][j][k] + dpThreeD[i - 1][j - group[i - 1]][k - profit[i - 1]]) % MOD;
                    } else {
                        dpThreeD[i][j][k] = dpThreeD[i - 1][j][k];
                    }
                }
            }             
        }
        
        int sum = 0;
        for(int j = 0; j <= n; j++) {
            sum = (sum + dpTwoD[len][j]) % MOD;
            for(int k = 0; k < minProfit; k++) {
                sum = (sum - dpThreeD[len][j][k]) % MOD;
            }   
        }

        return (sum + MOD) % MOD;
    }
};

复杂度分析

  • 时间复杂度: O ( l e n × n × m i n P r o f i t ) O(len \times n \times minProfit) O(len×n×minProfit),其中 $len $ 是数组长度, n n n 是可使用员工数, m i n P r o f i t minProfit minProfit 是最小利润。
  • 空间复杂度: O ( l e n × n × m i n P r o f i t ) O(len \times n \times minProfit) O(len×n×minProfit),其中 l e n len len 是数组长度, n n n 是可使用员工数, m i n P r o f i t minProfit minProfit 是最小利润,实现动态规划需要创建 n × m i n P r o f i t n \times minProfit n×minProfit 的二维数组 d p T w o D dpTwoD dpTwoD l e n × n × m i n P r o f i t len \times n \times minProfit len×n×minProfit 的三维数组 d p T h r e e D dpThreeD dpThreeD

拓展

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值