【编程题目】输入两个整数 n 和 m,从数列 1,2,3.......n 中 随意取几个数, 使其和等于 m ... ★...

本文针对2010年中兴面试题进行了解析,题目要求从1至n的数列中找出所有组合,使这些组合的和等于给定的m。通过递归算法和动态规划两种方式给出了详细的解决方案。

第 21 题(数组)
2010 年中兴面试题
编程求解:
输入两个整数 n 和 m,从数列 1,2,3.......n 中 随意取几个数,
使其和等于 m ,要求将其中所有的可能组合列出来.

 

我的思路:

从小到大 依次拼凑 后面选的数字 必须比前面大 保证不重复
如: n = 4 m = 8
1 2 3 4 超过8 去掉最后一个数 导数第二个数加一
1 2 4 小于8 最后一个数等于 4 去掉最后一个数 导数第二个数加一
1 3 4 符合 输出 最后一个数等于 4 去掉最后一个数 导数第二个数加一
1 4 小于8 最后一个数等于 4 去掉最后一个数 导数第二个数加一
2 3 小于8
2 3 4 超过8 去掉最后一个数 导数第二个数加一
2 4 小于8 最后一个数等于 4 去掉最后一个数 导数第二个数加一
......

 

代码:

/*
第 21 题(数组)
2010 年中兴面试题
编程求解:
输入两个整数  n  和  m,从数列 1,2,3.......n  中  随意取几个数,
使其和等于  m ,要求将其中所有的可能组合列出来.
start time = 15:55
end time = 17:03
*/

#include <iostream>
using namespace std;


int FindCombine(int n, int m)
{
    int method = 0; //一共有多少方法
    if(m <= 0)
    {
        return 0;
    }
    else
    {
        int record[100]; //存储组合当前的取值
        for(int i = 0; i < 100; i++)
        {
            record[i] = i + 1;
        }
        int num = 0; //组合中数字的个数
        int sum = 0; //组合中数字加起来的和

        while(sum < m)
        {
            sum += record[num];
            num++;

            if(sum == m)
            {
                method++;
                printf("组合%d:", method);
                for(int i = 0; i < num; i++)
                {
                    printf("%d ", record[i]);
                }
                printf("\n");

                if(num == 1)
                {
                    return method;
                }
            }
            if((sum >= m) || (sum < m && record[num - 1] == n && num >= 2)) //如果大小超过m 或者 最后一个数字大小等于n 更新record
            {
                num--;
                sum -= record[num];
                num--;
                sum -= record[num];
                if(record[num] <= n)
                {
                    record[num]++;
                    for(int i = 1; i <= n - record[num]; i++)
                    {
                        record[i + num] = record[num] + i;  
                    }
                }
            }
            else if(sum < m && record[num - 1] == n && num == 1)
            {
                return method;
            }
        }
    }
}



int main()
{
    int w = FindCombine(50, 100);
    return 0;
}

 

惯例上网找别人的方法, 一看我就郁闷了, 居然可以用动态规划, 我的算法算是白学了, 到用的时候一点都想不起来。

http://blog.sina.com.cn/s/blog_7571423b01016707.html 里有个详细的分析

http://www.cnblogs.com/freewater/archive/2012/07/16/2593218.html 里有非常精简的代码

vector<int> factors;
void findFactor2(int sum,int n){
    if(sum<0||n<0)
        return ;
    if(sum==0){
        for(vector<int>::iterator iter=factors.begin();iter!=factors.end();++iter){
            cout<<*iter<<' ';
        }
        cout<<endl;
        return;
    }

    factors.push_back(n);//典型的01背包问题  
    findFactor2(sum-n,n-1);//放n,n-1个数填满sum-n 
    factors.pop_back();
    findFactor2(sum,n-1);//不放n,n-1个数填满sum   
}

 

<think>好的,我现在需要解决这个问题:输入一个整数n,然后输出1到n的。用户要求用Java实现。首先,我得确保自己理解题目要求。题目看起来简单,但可能有需要注意的地方。 首先,输入整数n应该是一个正整数吗?题目没有特别说明,但通常这种累加问题n都是正数。不过,如果n是0或者负数的话,应该怎么处理呢?比如,如果n是0,应该是0;如果是负数,可能也应该返回0或者报错。不过题目没有说明,可能需要按照常规处理,假设n是非负整数。或者题目可能保证输入是正整数,这样就不用处理其他情况了。需要看用户的具体需求,但题目描述里没有特别指出,所以可能在代码里需要考虑输入的有效性。 接下来,如何计算1+2+...+n的?最直接的方法是使用循环,从1加到n。但这样时间复杂度是O(n),对于大数可能效率不高。有没有更高效的方法?记得数学公式:等于n*(n+1)/2。这样可以直接计算,不用循环,时间复杂度O(1)。这样更高效,尤其是当n很大的时候,比如接近10^9,用循环可能超时,而公式法不会有问题。所以应该优先使用公式法。 然后,输入的处理。用户输入一个整数n,用Java的话可以用Scanner来读。需要注意的是,如果用户输入的不是整数,程序可能会抛出异常,但题目可能假设输入是合法的,所以不需要处理这种情况。代码中直接读即可。 然后,根据n的值计算。如果使用数学公式,需要注意n的类型。如果n很大,比如超过2^31-1的话,用int可能会导致溢出。不过题目中的n的范围可能有限,但Java的int是32位,最大到2147483647。如果n是这个值,那么n*(n+1)/2可能会超过int的范围,导致溢出。这时候应该用long类型来计算。例如,将n转换为long,或者直接声明变量为long类型。假设输入的n在int范围内,那么n+1可能超过int的范围吗?比如当n是2147483647时,n+1就会溢出成负数,导致错误。所以必须将n转换为long类型再进行计算,避免溢出。 所以正确的步骤应该是: 1.整数n。 2. 检查n是否为非负。如果是负数,可能需要处理,但题目可能保证输入合法。 3. 使用公式sum = n*(n+1)/2,这里n需要转换为long,以防止溢出。 4. 输出结果。 那Java代码的结构大致如下: import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); long sum = (long) n * (n + 1) / 2; System.out.println(sum); } } 这样处理的话,当n是int最大值时,计算时先将n转换为long,这样n+1也会作为long计算,不会溢出。例如,当n=2147483647时,(long) n是2147483647L,n+1会变成2147483648L,相乘再除以2,结果正确。 但如果用户输入的n是负数呢?比如输入-5,这时候计算的结果会是负数,但题目可能要求n是正整数。这时候是否需要处理?题目描述中说“读入1整数n”,并没有说明n是否为正,所以可能需要考虑这种情况。例如,如果n是负数,应该输出0吗?或者题目保证n是正整数?用户的问题描述中可能假设n是正整数,所以代码可能不需要处理这种情况。但为了程序的健壮性,可以添加判断,如果n<=0,则输出0。例如: sum = (n > 0) ? (long) n * (n + 1) / 2 : 0; 但题目是否这样要求?原题描述中没有说明,所以可能需要根据常规情况处理。比如,题目示例可能当n为0时为0,负数可能视为无效,输出0。但用户的问题描述可能期望当n是任何整数时都计算,但根据数学定义,当n<=0时为0。例如,如果n是0,1+..+0的是0;如果n是负数,则没有数相加,是0。所以,正确的处理应该是当n<=0时输出0,否则用公式计算。或者,数学上,如果n是正整数就是n(n+1)/2,否则是0? 例如,当n=0时,1+2+...+0的应该不存在,但题目可能需要输出0。这时候可能需要判断n是否>=1,否则输出0。或者按照数学上的扩展定义,允许n为0或负数,此时为0。例如,根据公式,当n=0时,0*(0+1)/2=0,所以结果正确。当n为负数时,如n=-3,公式计算的结果是 (-3)*(-2)/2=3,但实际没有数的,所以这时候可能需要处理。所以必须明确题目的要求。 如果题目中的n是自然数,即n>=1,那么不需要处理。否则,当n<=0时,应输出0。但原问题描述中用户并没有明确这一点,因此代码可能需要包含这种情况的处理。例如: if (n < 1) { System.out.println(0); } else { // 使用公式计算 } 或者,使用公式计算,当n为负数时,结果可能不正确,所以需要处理。 这可能导致代码的不同。所以需要明确问题要求。根据用户的问题描述,“输入1整数n,然后输出1+2+3+...+n的”,假设n是自然数,包括0的话,当n=0时,是0。比如,数学上,当n=0时,这个应该等于0吗?或者当n是0时,是否没有数相加,所以为0?可能题目希望无论n是否为0或负数,都应用公式计算。例如,当n是-5,按公式得到(-5)*(-4)/2=10,但显然这不正确,因为原式中的在这种情况下应该不存在,或者视为0。所以这种情况下需要调整。 但可能题目中的n是给定的正整数,所以不需要处理这种情况。比如,题目测试用例中的n都是正整数。所以在这种情况下,代码可以直接使用公式,不管n是否为负数。不过根据数学定义,原式当n<=0时的是0吗?或者是否需要特殊处理? 这个时候可能需要根据题目的具体要求。例如,在编程竞赛中,题目通常会有输入约束,说明n>=1。但如果没有的话,程序应该处理所有可能的输入。这时候,正确的做法可能是:当n为负数时,输出0,否则用公式计算。或者,不管n的值如何,都按照公式计算,但这样当n是负数时会得到正数结果,可能不符合预期。 例如,当n=3,结果是6;n=0,公式给出0*(0+1)/2=0;n=-1,公式得到(-1)*0/2=0,这可能符合预期,即当n<=0时输出0。所以,直接使用公式可能自动处理了这些情况,不需要额外的判断。比如: 如果输入n=0,得到0;n=-5,得到(-5)*(-4)/2=10,这可能不对。所以这决于题目的意图。例如,题目可能要求当n是正整数时计算,否则输出0。这时候必须添加条件判断。 但用户的问题描述没有特别说明,所以需要假设输入的是合法的正整数。或者,可能需要根据常规处理,例如,当n<=0时输出0。这时候代码可以修改为: long sum = (n > 0) ? (long) n * (n + 1) / 2 : 0; 或者,更严格地说,当n >=1时计算,否则0。这时候: sum = (n >=1) ? ... : 0; 例如,当n=0时输出0,当n=1输出1,当n=2输出3,当n=-3输出0。 这种情况下,是否需要处理?这决于题目的具体要求。用户的问题描述中的例子可能假设n是正整数,所以代码可以不加判断,直接使用公式。例如,如果输入n是负数,输出的是负数对应的计算结果,这可能不符合预期。比如,n=-3,得到(-3)*(-2)/2=3,但实际没有数相加,所以正确的结果应该是0。因此,在这种情况下,应该添加条件判断,只有当n>=1时才计算,否则输出0。这样更符合实际需求。 所以,现在需要重新调整代码逻辑:先判断n是否大于等于1。如果是,则计算;否则输出0。 所以,修改后的代码: import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); if (n < 1) { System.out.println(0); } else { long sum = (long) n * (n + 1) / 2; System.out.println(sum); } } } 这样处理的话,当n是负数或者0时,输出0;当n是正数时计算正确的。例如: 输入5,输出15;输入0,输出0;输入-5,输出0。 这可能更符合题意,因为题目中的例子应该是正整数的情况。 或者,可能题目中的n允许为0,当n=0时为0,而无需特别处理。这时候,原公式依然正确,所以可以不用判断。例如,当n=0时,0*(0+1)/2=0,正确。当n=-1时,-1*0/2=0,可能不符合实际,但用户的问题可能不需要处理这种情况,所以可能直接按照公式计算即可。 这个时候,用户可能希望不管输入什么整数,都按照公式计算。例如,输入n=-3,输出3,这可能不符合题目的实际要求。所以必须根据题目的预期来处理。但由于题目描述中没有明确说明,可能需要用户自己判断。 假设题目中的n是自然数,即n>=1,那么不需要处理其他情况。例如,题目中的测试用例都是n>=1的情况,那么代码可以直接使用公式。 综上,正确的做法可能分两种情况: 1. 假设输入n>=1,直接用公式计算。 2. 考虑所有可能的整数输入,当n<=0时输出0。 但用户的问题描述中是否包含这些情况?可能题目希望处理所有情况,包括n为0或负数的情况,所以更健壮的代码应该处理这些情况。例如,当n是0时,为0;当n是负数时,为0。此时,在代码中添加条件判断。 现在,我需要总结正确的做法,并给出相应的Java代码。</think>以下是解决该问题的分步说明及Java代码实现: --- ### 分析步骤 1. **输入处理**:读一个整数$n$。 2. **输入验证**:若$n \leq 0$,为$0$;否则使用公式$S = \frac{n(n+1)}{2}$计算。 3. **防止溢出**:通过类型转换确保大数计算时不会溢出。 4. **结果输出**:直接输出计算结果。 --- ### Java代码实现 ```java import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); // 处理n为非正数的情况 if (n < 1) { System.out.println(0); return; } // 公式计算并防止溢出 long sum = (long) n * (n + 1) / 2; System.out.println(sum); } } ``` --- ### 代码解释 1. **输入**:使用`Scanner`类读整数$n$。 2. **边界条件处理**:若$n \leq 0$,直接输出$0$并结束程序。 3. **高效计算**:利用等差数列公式$S = \frac{n(n+1)}{2}$,时间复杂度为$O(1)$。 4. **类型转换**:将中间结果转换为`long`型,避免$n$较大时(如接近$10^9$)的溢出问题。 --- ### 示例测试 - **输入**:`5` **输出**:`15`(计算:$1+2+3+4+5=15$) - **输入**:`0` **输出**:`0`(非正数直接返回$0$) - **输入**:`100` **输出**:`5050`(验证公式正确性) 此方法兼顾效率健壮性,能正确处理所有可能的输入情况。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值