京东笔试——神奇数 【题目描述】东东在一本古籍上看到有一种神奇数,如果能够将一个数的数字分成两组,其中一组数字的和 等于另一组数字的和,我们就将这个数称为神奇数。例如 242 就是一个神奇数,我们能够

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36391130/article/details/81775824

2 、神奇数
【 题目描述】东东在一本古籍上看到有一种神奇数,如果能够将一个数的数字分成两组,其中一组数字的和
等于另一组数字的和,我们就将这个数称为神奇数。例如 242 就是一个神奇数,我们能够将这个数的数字分
成两组,分别是{2,2}以及{4},而且这两组数的和都是 4.东东现在需要统计给定区间中有多少个神奇数,即
给定区间[l, r],统计这个区间中有多少个神奇数,请你来帮助他。

输入描述: :
输入包括一行,一行中两个整数 l 和 r(1 ≤ l, r ≤ 10^9, 0 ≤ r - l ≤ 10^6),以空格分割
输出描述: :
输出一个整数,即区间内的神奇数个数

输入示例:
1 50
输出示例:
4

答案及解析:
方法一:(暴力求解)

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

#include<stdio.h>
#include<windows.h>

SYSTEMTIME time1, time2;///////////////计时

//两计时器相减得出程序运行时间
void my_count_and_print_time(SYSTEMTIME& time1, SYSTEMTIME& time2)
{
    printf("\n\n程序运行了 %d毫秒 \n", (1000 * time2.wSecond + time2.wMilliseconds) - (1000 * time1.wSecond + time1.wMilliseconds));
}
//将数n的每一位放入数组,将位数存入arr_num
void spr(int n, int arr[],int* arr_num)
{
    int elem = -1;
    int i = 0,j=0;
    while (n != 0)
    {
        elem = n % 10;
        n /= 10;
        arr[i++] = elem;
        ++(*arr_num);
    }
}
//判断是否为神奇数
bool is_shenqishu(int arr[],int* arr_num)
{
    int sum_bin = 0;
    int size = *arr_num;
    for (int i = 0; i < size; i++)
        sum_bin += arr[i];
    if (sum_bin % 2 != 0)
        return false;
    sum_bin = sum_bin/2;
    for (int one = 0; one < *arr_num; one++)
    {
        if (arr[one] == sum_bin)
            return true;
        for (int two = one + 1; two < *arr_num; two++)
        {
            if (arr[one] + arr[two] == sum_bin)
                return true;
            for (int three = two + 1; three < *arr_num; three++)
            {
                if (arr[one] + arr[two] + arr[three] == sum_bin)
                    return true;
                for (int four = three + 1; four < *arr_num; four++)
                {
                    if (arr[one] + arr[two] + arr[three] +arr[four]== sum_bin)
                        return true;
                    for (int five = four + 1; five < *arr_num; five++)
                    {
                        if (arr[one] + arr[two] + arr[three] + arr[four] + arr[five] == sum_bin)
                            return true;
                    }
                }
            }
        }
    }
    return false;
}

void jd_shenqishu()
{
    const int maxnum = (int)1e9;
    const int distance = (int)1e6;
    int left = 0;
    int right = 0;
    int arr[11];
    int result = 0;

    int arr_num = 0;
    scanf("%d %d", &left, &right);

    GetLocalTime(&time1);///////////////计时开始

    if (left<1 || right>maxnum || ((right - left) > distance) || left > right)
    {
        printf("输入不合要求!\n");
        return;
    }
    if (left < 10) left = 10;
    for (int n = left; n <= right; n++)
    {
        memset(arr, 0xffffffff, sizeof(arr));
        arr_num = 0;
        spr(n,arr,&arr_num);
        if (is_shenqishu(arr, &arr_num))
        {
            //printf("%4d \n", n);
            result++;
        }       
    }

    printf("\n 区间[ %d — %d ]内共有神奇数 %d 个 \n\n",left,right,result);
}
int main()
{

    jd_shenqishu();//区间:1-10^6  用时:151ms   神奇数个数:376413
    //jd_shenqishu_2();//区间:1-10^6  用时:488ms  神奇数个数:376413

    GetLocalTime(&time2);///////////////计时结束
    my_count_and_print_time(time1, time2);
    return 0;
}

代码看起来很烦,我简单来说明解决方法的思想是什么:
1.首先,题目中限定了输入的数字的区间和范围,由题目可知要判断是不是神奇数的数字最多不超过10位,我们就搞一个数组,用来存储数字分离出来的每一位。
2.接下来,我们要考虑的问题是,怎么判断是不是一个神奇数,题目中说,如果一个数字分成两组,两组之和相等,那么如果一个数的所有位之和是奇数,那它必然不是神奇数,因为它分成两组,两组之和必然不能相等。
3.然后,我们怎么判断所有位之和为偶数的数字是不是神奇数呢?这个数被分为了两组,那么,我们就只需要判断其中一组的和是不是所有位之和的一半就好了。如果是,那就是神奇数。

举两个例子:
数字:25732(2+3+7+5+2==19,19是奇数,所以2,5,7,3,2必然不能分为和相等的两组,所以不是神奇数)
数字:12452(2+5+4+2+1==14,14是偶数,再判断是否存在一个组合,组合内数字的和为7(14/2==7),很明显2+5==7,于是12452是神奇数)

有了上面的思路,问题的解决方法就很清晰了,再有的绊脚石就是代码中的算法了。
我们需要这些算法:1.将一个数字的每一位分离出来。2.将数组中的数字任意组合为一组然后组内求和。

第1个很简单,先对这个数模10 得到个位,然后再除10,将已经取得的个位去掉,然后之前的十位又变成了个位,就这样循环不停的搞它,直到它变成0,我们就取得了它的每一位。
举个栗子:数字245,245%10等于5,我们就得到了个位的5,赶紧存起来,然后245/10等于24 就把已经得到的5给抛弃了;接下来再24%10等于4,得到了4,24/10等于2抛弃了4;最后2%10 得到2,2/10等于0了,就循环结束,这时候数组里就存的5 4 2。
第2个也不难,这个描述起来有点绕,我们画个图,看一眼就知道怎么回事了。
这里写图片描述
我解释一下这张图,选了数字12452来判断其是否为神奇数(数组为arr[11],不放有效位的空间我们放置-1),它的组合方式有多少种不难算,就全组合(C 5,1 + C 5,2 + C 5,3 + C 5,4 等于 30 种 )可是呢,我们就能发现,它的第一种和最后一种是殊途同归,虽然第一种是选1个,第四个是选4个。但是,两组并不做分别,所以就有1倍的重复。于是我们就能发现,5位的数,我们只要做到两位的全组合,3位以后的组合方法已经全部囊括再2位以内了。当然在写循环时,将选两位的方式叠在选一位的方式内,将选三位的方式叠在选三位的方式内就很方便。(如果你不知道这句话啥意思,亲自写代码解这道题,你就知道了)

到这里,我们这种暴力求解的方法就赤裸裸的站在我们面前了。
接下来,我们来看第二种方法:
方法二:

#include<stdio.h>
#include<windows.h>

SYSTEMTIME time1, time2;///////////////计时

void my_count_and_print_time(SYSTEMTIME& time1, SYSTEMTIME& time2)
{
    printf("\n\n程序运行了 %d毫秒 \n", (1000 * time2.wSecond + time2.wMilliseconds) - (1000 * time1.wSecond + time1.wMilliseconds));
}
void spr(int n, int arr[],int* arr_num)
{
    int elem = -1;
    int i = 0,j=0;
    while (n != 0)
    {
        elem = n % 10;
        n /= 10;
        arr[i++] = elem;
        ++(*arr_num);
    }
}
bool is_shenqishu_2(int arr[], int* arr_num)
{
    bool judge_arr[42] = { 0 };
    int sum_bin = 0;
    int size = *arr_num;
    for (int i = 0; i < size; i++)
        sum_bin += arr[i];
    if (sum_bin % 2 != 0)
        return false;
    sum_bin = sum_bin / 2;

    judge_arr[arr[0]] = true;
    for (int i = 1; i < size; i++)
    {
        int v = arr[i];
        for (int j = 41; j >= 0; j--)
        {
            if (judge_arr[j] && (j + v <= 41))
            {
                judge_arr[j + v] = true;
            }
        }
    }
    if (judge_arr[sum_bin] == true)
        return true;
    return false;
}
void jd_shenqishu_2()
{
    const int maxnum = (int)1e9;
    const int distance = (int)1e6;
    int left = 0;
    int right = 0;
    int arr[11];
    int result = 0;
    //printf("%d ", maxnum);
    int arr_num = 0;
    scanf("%d %d", &left, &right);
    printf("\n 区间[ %d — %d ]", left, right);

    GetLocalTime(&time1);///////////////计时开始

    if (left<1 || right>maxnum || ((right - left) > distance || (right < left)))
    {
        printf("输入不合要求!\n");
        return;
    }

    if (left < 10) left = 10;
    if (right < 10) right = 10;

    for (int n = left; n <= right; n++)
    {
        memset(arr, 0xffffffff, sizeof(arr));
        arr_num = 0;
        spr(n, arr, &arr_num);
        if (is_shenqishu_2(arr, &arr_num))
        {
            //printf("%4d \n", n);
            result++;
        }
    }
    printf("内共有神奇数 %d 个 \n\n",result);
}

int main()
{
    GetLocalTime(&time1);///////////////计时开始

    //jd_shenqishu();//1-10^6 用时:151ms  376413
    jd_shenqishu_2();//1-10^6 用时:488ms  376413

    GetLocalTime(&time2);///////////////计时结束
    my_count_and_print_time(time1, time2); 
    return 0;
}

(如果没有搞懂第一种方法,可能看不懂第二种,因为我在说明第二种时一些细节没提,这些细节在第一种方法里说明了)
第二种方法其实大致与第一种方法相同,只是在判断是否为神奇数这里有点区别,我在这里就介绍第二种方法是怎样判断是否为神奇数的。
1.首先和为奇数就不是神奇数了,第一种方法已经说明原因。
2.接下来,就不像第一种方法那样对其分组了,而是直接来算所有 可能的 并且 不重复的 分组的和,再将sum/2与其比较。听起来不知道啥意思是不是,来画图说明。
这里写图片描述
如果没看明白,你自己搞着画一遍,这个流程就很清晰了。
那么就有个问题:judge_arr要多大呢?我们考虑和最大的数也就是999999999,和最大是81,81/2 就是40.5,我们取42,超过41的值我们就不用考虑。
这时候,就有点感觉好像不太靠谱的样子,那么,为什么这样就能判断是不是一个神奇数呢?原因其实也简单,我们顺序的将数组里的数字放入judge_arr然后将后面的数与前面的相加,其实加出来的结果不都是数组里数字的和吗,而且结合之前第一种的重复的情况,忽略一些重复的计算,所以这样可以判断是否是神奇数。

可以看到,在程序里,我分别对两种解法的运行时间做了计算(测试如下表),第一种方法很暴力很无脑,却很快,第二种方法巧一点,但是因为那个judge_arr[]速度慢很多,好像是想空间换时间,结果弄巧成拙。
这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_36391130/article/details/81775824