【问题标题】:Get all possible combinations to get a given sum with no repeating numbers获取所有可能的组合以获得不重复数字的给定总和
【发布时间】:2016-10-11 02:18:17
【问题描述】:

我正在尝试解决数字问题:

我收到一个数字,并且必须计算加起来才能得到该数字的数字,有一些规则使它变得更难

规则:

  • 只有正数,总和中不能包含 0
  • 总和只能由 6 个数字组成,不能多也不能少
  • 要添加的数字可以从 1 到 45
  • 不能重复数字
  • 最大总和为 255
  • 最小总和为 21
  • 有效的组合是 1、2、3、4、5、6 和 6、5、4、3、2、1 或 3、4、5、1、6、2,但只能算作一个组合,因为包含相同的数字但顺序不同

我一直在尝试像背包问题那样做,但不同的是我必须选择固定数量的数字才能得到总和。

如果有人有解决此问题的算法的想法,我将不胜感激。

【问题讨论】:

  • 是的,和那个类似。我试图让它基于该算法工作
  • 那么到目前为止你尝试过什么?
  • 目前我正在生成所有组合,但重复数字并使用或多或少的允许数字来获得总和

标签: java algorithm search combinations probability


【解决方案1】:

你可以使用动态规划来解决这个问题。

图中dp[N][LastNumber][ElementCount] 是多少种方法可以产生N,最后一个数字是LastNumber,元素个数是ElementCount。与N = 1..255LastNumber = 1..45ElementCount = 1..6

您可以从子解决方案中获得dp[N][LastNumber][ElementCount] dp[N-LastNumber][1][ElementCount-1] + dp[N-LastNumber][2][ElementCount-1] ... + dp[N-LastNumber][LastNumber-1][ElementCount-1]

基本情况是dp[i][i][1] = 1i = 1..45

如果问总结M有多少种方法,答案是dp[M][i][6]i = 1..45

【讨论】:

    【解决方案2】:

    在 Java 中: 如果您需要列出组合:

    static void sumToValue(int limit, int sum, int count, List<Integer> resultIP) {
        if (limit >= 0 && sum == 0 && count == 0) {
            // print resultIP, because it is one of the answers.
            System.out.println("sum(" + Arrays.toString(resultIP.toArray()) + ")");
        } else if (limit <= 0 || count == 0 || sum <= 0) {
            // not what we want
            return;
        } else {
            // Two options: choose current limit number or not
            sumToValue(limit - 1, sum, count, resultIP);// Not choose the limit
                                                        // number
    
            // or choose the limit number
            List<Integer> resultNext = new ArrayList<Integer>(resultIP);// copy
                                                                        // resultIP
            resultNext.add(limit);
            sumToValue(limit - 1, sum - limit, count - 1, resultNext);
        }
    }
    

    如果你只需要计数:

    static void sumToValueCount(int limit, int sum, int count) {
        int dp[][][] = new int[limit + 1][sum + 1][count + 1];
        for (int i = 0; i <= limit; i++) {
            for (int j = 0; j <= sum; j++) {
                for (int k = 0; k <= count; k++) {
                    if (j == 0 && k == 0) {
                        dp[i][j][k] = 1;
                    } else if (i == 0 || j <= 0 || k == 0) {
                        dp[i][j][k] = 0;
                    } else {
                        // check to prevent negative index
                        if (j - i >= 0) {
                            // two options: choose the number or not choose the number
                            dp[i][j][k] = dp[i - 1][j - i][k - 1] + dp[i - 1][j][k];
                        } else {
                            dp[i][j][k] = dp[i - 1][j][k];
                        }
                    }
                }
            }
        }
        System.out.println(dp[limit][sum][count]);
    }
    

    在这样的主函数调用中:

    //limit is 45, sum is the sum we want, count is 6 referring to the question.
    sumToValue(45, 255, 6, new ArrayList<Integer>());
    sumToValueCount(45, 255, 6);
    

    【讨论】:

      【解决方案3】:

      这是我在 C++ 中使用动态编程编写的代码。 n 是要添加的最大数量。 m 是元素计数,s 是目标总和。

      #include <iostream>
      #include <algorithm>
      #include <vector>
      
      using namespace std;
      
      int mini(int n, int m) {
          return m * (m + 1) / 2;
      }
      int maxi(int n, int m) {
          return m * (2 * n - m + 1) / 2;
      }
      
      typedef std::vector<unsigned long long> Long1D;
      typedef std::vector<Long1D> Long2D;
      typedef std::vector<Long2D> Long3D;
      
      int main(int argc, const char * argv[]) {
          int n, m, s;
          n = 45;
          m = 6;
          s = 21;
      
          if ((s < mini(n, m)) || (s > maxi(n, m))) {
              cout << 0 << endl;
              return 0;
          }
      
          Long3D dp(2, Long2D(m + 1, Long1D(s + 1)));
      
          for (int i = 1; i <= n; ++i) {
              for (int j = 1; j <= min(i, m); ++j) {
                  for (int k = 1; k <= s; ++k) {
                      if ((k < mini(i, j)) || (k > maxi(i, j))) {
                          dp[i % 2][j][k] = 0;
                      }
                      else if ((k == mini(i, j)) || (k == maxi(i, j)) || j == 1) {
                          dp[i % 2][j][k] = 1;
                      }
                      else {
                          dp[i % 2][j][k] = 0;
                          // !IMPORTANT -- general situation: dp[i][j][k]=dp[i-1][j-1][k-j]+dp[i-1][j][k-j]
                          if (k - j > mini(i - 1, j - 1))
                              dp[i % 2][j][k] += dp[(i - 1) % 2][j - 1][k - j];
                          if (k - j < maxi(i - 1, j))
                              dp[i % 2][j][k] += dp[(i - 1) % 2][j][k - j];
                      }
                  }
              }
          }
      
          cout << dp[n % 2][m][s] << endl;
          return 0;
      }
      

      【讨论】:

      • 虽然此链接可能会回答问题,但it is better 在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效
      • 此编辑使您的答案可能更好,但您可以删除不再需要的链接,您也可以考虑添加更多解释以帮助未来的读者更有效地理解您的代码
      • @icer 的答案可能工作得很好而且很干净,并且至少更容易掌握或解释。我给他赏金。我试图提供帮助,但我绝对不觉得受到欢迎或赞赏。对不起,你可以删除这篇文章。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-24
      • 1970-01-01
      • 1970-01-01
      • 2017-09-18
      • 1970-01-01
      • 2011-06-05
      相关资源
      最近更新 更多