简体   繁体   English

获取所有可能的组合以获得给定的总和,没有重复的数字

[英]Get all possible combinations to get a given sum with no repeating numbers

I am trying to solve a problem with numbers: 我正在尝试解决数字问题:

I receive a number and have to calculate the numbers that add up to get that number, there are some rules that make it harder 我收到一个数字,必须计算得出的数字总和,所以有些规则使它更难

Rules: 规则:

  • Positive numbers only, cannot include 0 in the sum 仅正数,总和不能包含0
  • The sum must be composed of only 6 numbers, no more no less 总和只能由6个数字组成,且不得少于或等于
  • The numbers to add can go from 1 to 45 要添加的数字可以从1到45
  • Cannot repeat numbers 不能重复数字
  • The maximum sum is 255 最大和为255
  • The minimum sum is 21 最低和为21
  • A valid combination is 1, 2, 3, 4, 5, 6 just as 6, 5, 4, 3, 2, 1 or 3, 4, 5, 1, 6, 2 but only counts as one combination because contain the same numbers but in different order 有效组合为1、2、3、4、5、6,就像6、5、4、3、2、1或3、4、5、1、6、2一样,但仅计为一个组合,因为包含相同的组合数字,但顺序不同

I have been trying to do it like in the knapsack problem but the difference is that I must choose a fixed amount of numbers to get the sum. 我一直试图像背包问题那样做,但是不同之处在于我必须选择固定数量的数字才能得出总和。

If anyone has an idea of an algorithm to fix this, I would really appreciate it. 如果有人对解决此问题的算法有所了解,我将非常感谢。

You can use dynamic programming to solve this problem. 您可以使用动态编程来解决此问题。

Figure that dp[N][LastNumber][ElementCount] is How many ways to yield N with the last number is LastNumber and the number of element is ElementCount . 如图所示, dp[N][LastNumber][ElementCount]是产生N的最后几种方法是LastNumberLastNumberElementCount With N = 1..255 , LastNumber = 1..45 , ElementCount = 1..6 如果N = 1..255LastNumber = 1..45ElementCount = 1..6

You can get dp[N][LastNumber][ElementCount] from subsolution dp[N-LastNumber][1][ElementCount-1] + dp[N-LastNumber][2][ElementCount-1] ... + dp[N-LastNumber][LastNumber-1][ElementCount-1] 你可以得到dp[N][LastNumber][ElementCount]从下解dp[N-LastNumber][1][ElementCount-1] + dp[N-LastNumber][2][ElementCount-1] ... + dp[N-LastNumber][LastNumber-1][ElementCount-1]

The base case is dp[i][i][1] = 1 for i = 1..45 对于i = 1..45 ,基本情况为dp[i][i][1] = 1

if it is asked how many ways to sum up M , the asnwer is dp[M][i][6] for i = 1..45 如果询问求和M方式有多少种,则对i = 1..45dp[M][i][6]

In Java: If you need to list the combinations: 在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);
    }
}

If you only need the count: 如果只需要计数:

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]);
}

In main function call like this: 在这样的主函数调用中:

//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);

Here is the code I come up with in C++ using dynamic programming. 这是我使用动态编程在C ++中想到的代码。 n is the maximum number to add. n是要添加的最大数量。 m is the element count and s is the target sum. 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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM