获取所有可能的组合以获得给定的没有重复数字的总和
Get all possible combinations to get a given sum with no repeating numbers
我正在尝试解决数字问题:
我收到一个数字,必须计算加起来才能得到该数字的数字,有些规则使它变得更难
规则:
- 只能是正数,总和不能包含0
- 求和只能由6个数组成,不能多也不能少
- 要添加的数字可以是 1 到 45
- 不能重复数字
- 最大总和为255
- 最小金额为 21
- 有效组合是 1、2、3、4、5、6,就像 6、5、4、3、2、1 或 3、4、5、1、6、2,但只算作一个组合,因为包含相同的数字但顺序不同
我一直在尝试像背包问题那样做,但不同的是我必须选择固定数量的数字来求和。
如果有人知道解决此问题的算法,我将不胜感激。
你可以使用动态规划来解决这个问题。
图中dp[N][LastNumber][ElementCount]
是How many ways to yield N
,最后一个数是LastNumber
,元素个数是ElementCount
。用N = 1..255
、LastNumber = 1..45
、ElementCount = 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]
i = 1..45
的基本情况是 dp[i][i][1] = 1
如果问有多少种方式求和M
,答案是dp[M][i][6]
for i = 1..45
这是我使用动态编程在 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;
}
在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);
我正在尝试解决数字问题:
我收到一个数字,必须计算加起来才能得到该数字的数字,有些规则使它变得更难
规则:
- 只能是正数,总和不能包含0
- 求和只能由6个数组成,不能多也不能少
- 要添加的数字可以是 1 到 45
- 不能重复数字
- 最大总和为255
- 最小金额为 21
- 有效组合是 1、2、3、4、5、6,就像 6、5、4、3、2、1 或 3、4、5、1、6、2,但只算作一个组合,因为包含相同的数字但顺序不同
我一直在尝试像背包问题那样做,但不同的是我必须选择固定数量的数字来求和。
如果有人知道解决此问题的算法,我将不胜感激。
你可以使用动态规划来解决这个问题。
图中dp[N][LastNumber][ElementCount]
是How many ways to yield N
,最后一个数是LastNumber
,元素个数是ElementCount
。用N = 1..255
、LastNumber = 1..45
、ElementCount = 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]
i = 1..45
dp[i][i][1] = 1
如果问有多少种方式求和M
,答案是dp[M][i][6]
for i = 1..45
这是我使用动态编程在 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;
}
在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);