2058 The sum problem

题目

Problem Description
Given a sequence 1,2,3,…N, your job is to calculate all the possible sub-sequences that the sum of the sub-sequence is M.

Input
Input contains multiple test cases. each case contains two integers N, M( 1 <= N, M <= 1000000000).input ends with N = M = 0.

Output
For each test case, print all the possible sub-sequence that its sum is M.The format is show in the sample below.print a blank line after each test case.

Sample Input
20 10
50 30
0 0

Sample Output
[1,4]
[10,10]
[4,8]
[6,9]
[9,11]
[30,30]

暴力解法

这是最容易想到的一种解法,但超时了

#include <cstdio>
int main() {
    int N, M, sum;
    while (scanf("%d%d", &N, &M), (N != 0 && M != 0)) {
        for (int i = 1;i <= N;i++) {
            sum = 0;
            for (int j = i;j <= N;j++) {
                sum += j;
                if (sum == M) {
                    printf("[%d, %d]\n", i, j);
                }
            }
        }
        printf("\n");
    }
}

数学知识

如果想要进行一些优化,那就要补充一些数学知识了

考虑这样的一个连续的整数序列

  • 以 n 为中间数,向左右分别取 m 个数

对这个序列求和

sum = (n-m) + (n-m+1) + … + (n-1) + n + (n+1) + … + (n+m-1) + (n+m) = (2m+1) × n

也就是说,如果一个数 X 能被 2m+1 整除,且 n = X / (2m+1)

那么这个数 X 就等于从 n-m 到 n+m 的连续整数序列的和

即 X = (n-m) + (n-m+1) + … + (n-1) + n + (n+1) + … + (n+m-1) + (n+m)

换句话说,如果一个数 X 能被一个奇数 i 整除,此时 n = x / i, m = (i-1)/2,

那么这个数 X 就等于从 x/i - (i-1)/2 到 x/i + (i-1)/2 的连续整数序列的和

  • 以 n 和 n+1 为中间数,向两边分别取 m 个数

对这个序列求和

sum = (n-m) + (n-m+1) + … + n + (n+1) + … + (n+m+1) = (m+1)(2n+1)

也就是说,如果一个数 X 能被 2n+1 整除,且 n = X / (2m+2)

这里 x / (2m+2) 得到的结果实际上是 n 和 n+1 的平均数,由于舍弃了小数,所以正好等于 n
故可以用 (X - X / (2m+2) * (2m+2)) == 2m+2 作为判断条件

那么这个数 X 就等于从 n-m 到 n+m+1 的连续整数序列的和

即 X = (n-m) + (n-m+1) + … + n + (n+1) + … + (n+m+1)

代码优化

有了上面的数学知识,我们就可以这样来写

对于 1,2,3,4,···,n 这样的连续整数序列,

我们首先要确定子序列可能的最大长度,

然后,根据长度,依次判断是否存在该长度对应的子序列

#include<stdio.h>
int main() {
    int n, m, i;
    while (scanf("%d%d", &m, &n), m + n) {
        // n                    子序列之和
        // i                    子序列长度
        // n / i                子序列的中间那个数
        // n / i - (i - 1) / 2  子序列的最左边的那个数
        // 在保证子序列最左边的数大于零的条件下确定 i 的最大值
        for (i = 1; n / i - (i - 1) / 2 > 0; i++);
        // i 不为 0 && 子序列最右边的数不超过原数列边界
        for (i--; i && n / i + i / 2 <= m; i--) {
            // 当 n 不能整除 i && 2倍的余数等于长度
            if ((n - n / i * i) * 2 == i)
                printf("[%d,%d]\n", n / i - (i - 1) / 2, n / i + i / 2);
            // 当 n 能整除 i && i 为奇数
            if (!(n % i) && i % 2)
                printf("[%d,%d]\n", n / i - (i - 1) / 2, n / i + i / 2);
        }
        putchar('\n');
    }
    return 0;
}
  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值