问题描述
个人认为原问题的范围应该是不可靠的,只要过了给定的用例就可以
题目简述
要求输出n个正整数,他们的和为s, 每个元素值的范围在[1,m]之内。求输出所有可能的组合结果。
输入: n = 2, m = 6, s = 10,下面简述为(n, m, s), 即(2,6,10)
输出:
(4,6)
(5,5)
注意:
(6,4)属于重复的结果
边界约束:
1 <= n <= 100
1 <= m <= 1e9
1 <= s <= 1e5
测试用例:
(2, 6, 10)
(6, 6, 8)
(3, 1e5, 20)
(10, 10, 100)
(100, 100, 10000)
可行思路
vector<vector<int>> res;
void helper(int pos, int left, const int m, vector<int>&tmp) {
if (pos == tmp.size()) {
if (left == 0) {
auto cpy = tmp;
//std::sort(cpy.begin(), cpy.end());
res.emplace_back(std::move(cpy));
}
return;
}
// 剪枝1: 后面的值必定不小于前面的值(保证不重)
int meta_x = pos > 0 ? tmp[pos - 1] : 1;
// 剪枝2: 最后一个槽不再穷举
if (pos == tmp.size() - 1) {
// 触发写入
if (left <= m) {
tmp[pos] = left;
helper(pos + 1, 0, m, tmp);
}
return;
}
int l = meta_x;
int r = m;
int left_cnt = tmp.size() - 1 - pos; // 剩余槽位
// 剪枝3: 余数全取最小值不满足题意筛掉
if (meta_x * (left_cnt + 1) > left) return;
// 剪枝4: 二分查找过滤无效边界
while (l <= r) {
int x = (r - l) / 2 + l;
// 以x作余数均值其和大于left
if (left - x < 0 || x * left_cnt > left - x) {
r = x - 1;
}// 余数均值大于m
else if ((left - x) > m * left_cnt) {
l = x + 1;
}
else {
while ((left - l) > m * left_cnt) {
l++;
}
tmp[pos] = l;
helper(pos + 1, left - l, m, tmp);
l++;
}
}
}
void Hello(int n, int m, int s) {
vector<int> tmp(n, 0);
int left = s;
int pos = 0;
printf("===%d,%d,%d===\n", n, m, s);
helper(0, s, m, tmp);
}