自然数拆分
题目:
给出sum、min、max和n四个正整数,请输出所有将sum拆分为n个递增的正整数(允许相等)之和,其中每个正整数k都满足:min <= k <= max。
在少侠的博客看到这道题,就随手做了下。该题与输出N个数取M个数的所有组合类似,只不过限定了M个数的和以及取值范围。可以先用贪心算法构造一个最小的组合,然后调整这个组合,得到下一个组合。显然应该从后往前找到一个数字,该数增加1,再调整该数后面的数。这个数就是:从后往前第一个与最后一个数的差值大于1的数(因为,如果差值小等于1,无法调整后面的数得到一个符合要求的组合)。
//www.cnblogs.com/flyinghearts
#include<iostream>
#include<vector>
#include<cassert>
using std::cout;
using std::vector;
static inline void print(int arr[], int len, int n)
{
cout << n << " = ";
for (int i = 0; i < len - 1; ++i) cout << arr[i] << " + ";
cout << arr[len - 1];
cout << "\n";
}
static inline void init(int arr[], int len, int sum, int min, int max)
{
sum -= min * len;
int i = len;
while ((sum -= max - min) > 0) arr[--i] = max;
arr[--i] = sum + max;
while (i > 0) arr[--i] = min;
}
void solve(int sum, int n, int min, int max)
{
if (n <= 1 || min > max || min <= 0 || sum < n * min || sum > n * max) return;
static vector<int> v;
v.resize(n);
int *first = &v[0];
int *last = &v[n - 1];
init(first, n, sum, min, max);
while (1) {
print(first, n, sum);
int *p = last - 1;
//若最后两个数差值大等于2,则直接调整
if (*last - *p >= 2) { ++*p; --*last; continue; }
int tt = *last - 2;
int ss = *last + *p;
//从后往前找到第一个与最后一个数差值大等于2的数,从该数开始调整
while (--p >= first && *p > tt) { ss += *p;}
if (p < first) break;
//if (*p == tt && *last != *(last - 1)) { ++*p; --*last; continue;}
++*p;
init(p + 1, last - p, ss - 1, *p, max);
}
cout << "\n";
}
int main()
{
solve(10, 4, 1, 5);
solve(10, 5, 2, 2);
}
#include<iostream>
#include<vector>
#include<cassert>
using std::cout;
using std::vector;
static inline void print(int arr[], int len, int n)
{
cout << n << " = ";
for (int i = 0; i < len - 1; ++i) cout << arr[i] << " + ";
cout << arr[len - 1];
cout << "\n";
}
static inline void init(int arr[], int len, int sum, int min, int max)
{
sum -= min * len;
int i = len;
while ((sum -= max - min) > 0) arr[--i] = max;
arr[--i] = sum + max;
while (i > 0) arr[--i] = min;
}
void solve(int sum, int n, int min, int max)
{
if (n <= 1 || min > max || min <= 0 || sum < n * min || sum > n * max) return;
static vector<int> v;
v.resize(n);
int *first = &v[0];
int *last = &v[n - 1];
init(first, n, sum, min, max);
while (1) {
print(first, n, sum);
int *p = last - 1;
//若最后两个数差值大等于2,则直接调整
if (*last - *p >= 2) { ++*p; --*last; continue; }
int tt = *last - 2;
int ss = *last + *p;
//从后往前找到第一个与最后一个数差值大等于2的数,从该数开始调整
while (--p >= first && *p > tt) { ss += *p;}
if (p < first) break;
//if (*p == tt && *last != *(last - 1)) { ++*p; --*last; continue;}
++*p;
init(p + 1, last - p, ss - 1, *p, max);
}
cout << "\n";
}
int main()
{
solve(10, 4, 1, 5);
solve(10, 5, 2, 2);
}
作者: flyinghearts
出处: http://www.cnblogs.com/flyinghearts/
本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。