题意:
给一个正整数N和长度L,找出一段长度大于等于L的连续非负整数,他们的和恰好为N。答案可能有多个,找出长度最小的那个。
例如:N=18 L=2:
5+6+7 = 18
3+4+5+6 = 18,输出更短的 5 6 7
数据范围:1 <= n <= 10^9, 2<= L<= 100
解法:暴力+剪枝
暴力枚举起始值和长度,双重for循环。由于需要找到长度尽量小的,所以从L开始枚举到100;起始值从0枚举到N。
剪枝:如果起始值为i,长度为len,则最后的值为i + len -1
从1开始到i的总和为f(i) = (1+i) * i /2
N = f(i+len - 1) - f(i),化简得到 i = N/(len - 1) - len/2
通过这个表达式可以缩小i的枚举范围。
代码:
#include <iostream>
using namespace std;
int main() {
int N, L;
int st, length;
bool found = false;
while (cin >> N >> L) { // 注意 while 处理多个 case
for(int len = L; len <= 100; len++) {
int n = N/(len-1) - len/2;
for(int i = n; i >= 0; i--) {
if((2 * i + len - 1) * len == 2 * N) {
st = i;
length = len;
found = true;
break;
}
}
if(found) break;
}
if(found) {
for(int i = 0; i < length - 1; i++) {
printf("%d ", st + i);
}
printf("%d\n", st + length - 1);
} else {
printf("No\n");
}
}
}
case举例:
5050 26
69564669 12 (1617762 ~ 1617804)
4950 100
1000000000 2 (199999998 ~ 200000002)