返回介绍

solution / 0800-0899 / 0879.Profitable Schemes / README

发布于 2024-06-17 01:03:33 字数 8823 浏览 0 评论 0 收藏 0

879. 盈利计划

English Version

题目描述

集团里有 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) 。

 

提示:

  • 1 <= n <= 100
  • 0 <= minProfit <= 100
  • 1 <= group.length <= 100
  • 1 <= group[i] <= 100
  • profit.length == group.length
  • 0 <= profit[i] <= 100

解法

方法一:记忆化搜索

我们设计一个函数 $dfs(i, j, k)$,表示从第 $i$ 个工作开始,且当前已经选择了 $j$ 个员工,且当前产生的利润为 $k$,这种情况下的方案数。那么答案就是 $dfs(0, 0, 0)$。

函数 $dfs(i, j, k)$ 的执行过程如下:

  • 如果 $i = n$,表示所有工作都已经考虑过了,如果 $k \geq minProfit$,则方案数为 $1$,否则方案数为 $0$;
  • 如果 $i \lt n$,我们可以选择不选择第 $i$ 个工作,此时方案数为 $dfs(i + 1, j, k)$;如果 $j + group[i] \leq n$,我们也可以选择第 $i$ 个工作,此时方案数为 $dfs(i + 1, j + group[i], \min(k + profit[i], minProfit))$。这里我们将利润上限限制在 $minProfit$,是因为利润超过 $minProfit$ 对我们的答案没有任何影响。

最后返回 $dfs(0, 0, 0)$ 即可。

为了避免重复计算,我们可以使用记忆化搜索的方法,用一个三维数组 $f$ 记录所有的 $dfs(i, j, k)$ 的结果。当我们计算出 $dfs(i, j, k)$ 的值后,我们将其存入 $f[i][j][k]$ 中。调用 $dfs(i, j, k)$ 时,如果 $f[i][j][k]$ 已经被计算过,我们直接返回 $f[i][j][k]$ 即可。

时间复杂度 $O(m \times n \times minProfit)$,空间复杂度 $O(m \times n \times minProfit)$。其中 $m$ 和 $n$ 分别为工作的数量和员工的数量,而 $minProfit$ 为至少产生的利润。

class Solution:
  def profitableSchemes(
    self, n: int, minProfit: int, group: List[int], profit: List[int]
  ) -> int:
    @cache
    def dfs(i: int, j: int, k: int) -> int:
      if i >= len(group):
        return 1 if k == minProfit else 0
      ans = dfs(i + 1, j, k)
      if j + group[i] <= n:
        ans += dfs(i + 1, j + group[i], min(k + profit[i], minProfit))
      return ans % (10**9 + 7)

    return dfs(0, 0, 0)
class Solution {
  private Integer[][][] f;
  private int m;
  private int n;
  private int minProfit;
  private int[] group;
  private int[] profit;
  private final int mod = (int) 1e9 + 7;

  public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
    m = group.length;
    this.n = n;
    f = new Integer[m][n + 1][minProfit + 1];
    this.minProfit = minProfit;
    this.group = group;
    this.profit = profit;
    return dfs(0, 0, 0);
  }

  private int dfs(int i, int j, int k) {
    if (i >= m) {
      return k == minProfit ? 1 : 0;
    }
    if (f[i][j][k] != null) {
      return f[i][j][k];
    }
    int ans = dfs(i + 1, j, k);
    if (j + group[i] <= n) {
      ans += dfs(i + 1, j + group[i], Math.min(k + profit[i], minProfit));
    }
    ans %= mod;
    return f[i][j][k] = ans;
  }
}
class Solution {
public:
  int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
    int m = group.size();
    int f[m][n + 1][minProfit + 1];
    memset(f, -1, sizeof(f));
    const int mod = 1e9 + 7;
    function<int(int, int, int)> dfs = [&](int i, int j, int k) -> int {
      if (i >= m) {
        return k == minProfit ? 1 : 0;
      }
      if (f[i][j][k] != -1) {
        return f[i][j][k];
      }
      int ans = dfs(i + 1, j, k);
      if (j + group[i] <= n) {
        ans += dfs(i + 1, j + group[i], min(k + profit[i], minProfit));
      }
      ans %= mod;
      return f[i][j][k] = ans;
    };
    return dfs(0, 0, 0);
  }
};
func profitableSchemes(n int, minProfit int, group []int, profit []int) int {
  m := len(group)
  f := make([][][]int, m)
  for i := range f {
    f[i] = make([][]int, n+1)
    for j := range f[i] {
      f[i][j] = make([]int, minProfit+1)
      for k := range f[i][j] {
        f[i][j][k] = -1
      }
    }
  }
  const mod = 1e9 + 7
  var dfs func(i, j, k int) int
  dfs = func(i, j, k int) int {
    if i >= m {
      if k >= minProfit {
        return 1
      }
      return 0
    }
    if f[i][j][k] != -1 {
      return f[i][j][k]
    }
    ans := dfs(i+1, j, k)
    if j+group[i] <= n {
      ans += dfs(i+1, j+group[i], min(k+profit[i], minProfit))
    }
    ans %= mod
    f[i][j][k] = ans
    return ans
  }
  return dfs(0, 0, 0)
}

方法二:动态规划

我们定义 $f[i][j][k]$ 表示前 $i$ 个工作中,选择了不超过 $j$ 个员工,且至少产生 $k$ 的利润的方案数。初始时 $f[0][j][0] = 1$,表示不选择任何工作,且至少产生 $0$ 的利润的方案数为 $1$。答案即为 $f[m][n][minProfit]$。

对于第 $i$ 个工作,我们可以选择参与或不参与。如果不参与,则 $f[i][j][k] = f[i - 1][j][k]$;如果参与,则 $f[i][j][k] = f[i - 1][j - group[i - 1]][max(0, k - profit[i - 1])]$。我们需要枚举 $j$ 和 $k$,并将所有的方案数相加。

最终的答案即为 $f[m][n][minProfit]$。

时间复杂度 $O(m \times n \times minProfit)$,空间复杂度 $O(m \times n \times minProfit)$。其中 $m$ 和 $n$ 分别为工作的数量和员工的数量,而 $minProfit$ 为至少产生的利润。

class Solution:
  def profitableSchemes(
    self, n: int, minProfit: int, group: List[int], profit: List[int]
  ) -> int:
    mod = 10**9 + 7
    m = len(group)
    f = [[[0] * (minProfit + 1) for _ in range(n + 1)] for _ in range(m + 1)]
    for j in range(n + 1):
      f[0][j][0] = 1
    for i, (x, p) in enumerate(zip(group, profit), 1):
      for j in range(n + 1):
        for k in range(minProfit + 1):
          f[i][j][k] = f[i - 1][j][k]
          if j >= x:
            f[i][j][k] = (f[i][j][k] + f[i - 1][j - x][max(0, k - p)]) % mod
    return f[m][n][minProfit]
class Solution {
  public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
    final int mod = (int) 1e9 + 7;
    int m = group.length;
    int[][][] f = new int[m + 1][n + 1][minProfit + 1];
    for (int j = 0; j <= n; ++j) {
      f[0][j][0] = 1;
    }
    for (int i = 1; i <= m; ++i) {
      for (int j = 0; j <= n; ++j) {
        for (int k = 0; k <= minProfit; ++k) {
          f[i][j][k] = f[i - 1][j][k];
          if (j >= group[i - 1]) {
            f[i][j][k]
              = (f[i][j][k]
                  + f[i - 1][j - group[i - 1]][Math.max(0, k - profit[i - 1])])
              % mod;
          }
        }
      }
    }
    return f[m][n][minProfit];
  }
}
class Solution {
public:
  int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
    int m = group.size();
    int f[m + 1][n + 1][minProfit + 1];
    memset(f, 0, sizeof(f));
    for (int j = 0; j <= n; ++j) {
      f[0][j][0] = 1;
    }
    const int mod = 1e9 + 7;
    for (int i = 1; i <= m; ++i) {
      for (int j = 0; j <= n; ++j) {
        for (int k = 0; k <= minProfit; ++k) {
          f[i][j][k] = f[i - 1][j][k];
          if (j >= group[i - 1]) {
            f[i][j][k] = (f[i][j][k] + f[i - 1][j - group[i - 1]][max(0, k - profit[i - 1])]) % mod;
          }
        }
      }
    }
    return f[m][n][minProfit];
  }
};
func profitableSchemes(n int, minProfit int, group []int, profit []int) int {
  m := len(group)
  f := make([][][]int, m+1)
  for i := range f {
    f[i] = make([][]int, n+1)
    for j := range f[i] {
      f[i][j] = make([]int, minProfit+1)
    }
  }
  for j := 0; j <= n; j++ {
    f[0][j][0] = 1
  }
  const mod = 1e9 + 7
  for i := 1; i <= m; i++ {
    for j := 0; j <= n; j++ {
      for k := 0; k <= minProfit; k++ {
        f[i][j][k] = f[i-1][j][k]
        if j >= group[i-1] {
          f[i][j][k] += f[i-1][j-group[i-1]][max(0, k-profit[i-1])]
          f[i][j][k] %= mod
        }
      }
    }
  }
  return f[m][n][minProfit]
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文